Skip to content
Snippets Groups Projects
base_cluster_invoker.go 6.34 KiB
Newer Older
AlexStocks's avatar
AlexStocks committed
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
fangyincheng's avatar
fangyincheng committed

vito.he's avatar
vito.he committed
package cluster_impl
vito.he's avatar
vito.he committed

import (
fangyincheng's avatar
fangyincheng committed
	perrors "github.com/pkg/errors"
vito.he's avatar
vito.he committed
	"go.uber.org/atomic"
vito.he's avatar
vito.he committed
)

import (
	"github.com/apache/dubbo-go/cluster"
	"github.com/apache/dubbo-go/common"
	"github.com/apache/dubbo-go/common/constant"
	"github.com/apache/dubbo-go/common/extension"
	"github.com/apache/dubbo-go/common/logger"
vito.he's avatar
vito.he committed
)

type baseClusterInvoker struct {
	directory      cluster.Directory
	availablecheck bool
vito.he's avatar
vito.he committed
	destroyed      *atomic.Bool
Ooo0oO0o0oO's avatar
Ooo0oO0o0oO committed
	stickyInvoker  protocol.Invoker
vito.he's avatar
vito.he committed
func newBaseClusterInvoker(directory cluster.Directory) baseClusterInvoker {
vito.he's avatar
vito.he committed
	return baseClusterInvoker{
		directory:      directory,
		availablecheck: true,
vito.he's avatar
vito.he committed
		destroyed:      atomic.NewBool(false),
Xin.Zh's avatar
Xin.Zh committed
func (invoker *baseClusterInvoker) GetURL() *common.URL {
	return invoker.directory.GetURL()
vito.he's avatar
vito.he committed
}

func (invoker *baseClusterInvoker) Destroy() {
	//this is must atom operation
vito.he's avatar
vito.he committed
	if invoker.destroyed.CAS(false, true) {
vito.he's avatar
vito.he committed
		invoker.directory.Destroy()
	}
}

func (invoker *baseClusterInvoker) IsAvailable() bool {
Ooo0oO0o0oO's avatar
Ooo0oO0o0oO committed
	if invoker.stickyInvoker != nil {
		return invoker.stickyInvoker.IsAvailable()
	}
vito.he's avatar
vito.he committed
	return invoker.directory.IsAvailable()
}

//check invokers availables
func (invoker *baseClusterInvoker) checkInvokers(invokers []protocol.Invoker, invocation protocol.Invocation) error {
	if len(invokers) == 0 {
		ip := common.GetLocalIp()
fangyincheng's avatar
fangyincheng committed
		return perrors.Errorf("Failed to invoke the method %v. No provider available for the service %v from "+
			"registry %v on the consumer %v using the dubbo version %v .Please check if the providers have been started and registered.",
Xin.Zh's avatar
Xin.Zh committed
			invocation.MethodName(), invoker.directory.GetURL().SubURL.Key(), invoker.directory.GetURL().String(), ip, constant.Version)
	}
	return nil

}

//check cluster invoker is destroyed or not
func (invoker *baseClusterInvoker) checkWhetherDestroyed() error {
vito.he's avatar
vito.he committed
	if invoker.destroyed.Load() {
		ip := common.GetLocalIp()
		return perrors.Errorf("Rpc cluster invoker for %v on consumer %v use dubbo version %v is now destroyed! can not invoke any more. ",
Xin.Zh's avatar
Xin.Zh committed
			invoker.directory.GetURL().Service(), ip, constant.Version)
	}
	return nil
}

func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
Ooo0oO0o0oO's avatar
Ooo0oO0o0oO committed
	var selectedInvoker protocol.Invoker
haohongfan's avatar
haohongfan committed
	if len(invokers) <= 0 {
		return selectedInvoker
	}

Xin.Zh's avatar
Xin.Zh committed
	url := invokers[0].GetURL()
Ooo0oO0o0oO's avatar
Ooo0oO0o0oO committed
	sticky := url.GetParamBool(constant.STICKY_KEY, false)
	//Get the service method sticky config if have
	sticky = url.GetMethodParamBool(invocation.MethodName(), constant.STICKY_KEY, sticky)

	if invoker.stickyInvoker != nil && !isInvoked(invoker.stickyInvoker, invokers) {
		invoker.stickyInvoker = nil
	}

haohongfan's avatar
haohongfan committed
	if sticky && invoker.availablecheck &&
		invoker.stickyInvoker != nil && invoker.stickyInvoker.IsAvailable() &&
		(invoked == nil || !isInvoked(invoker.stickyInvoker, invoked)) {
		return invoker.stickyInvoker
Ooo0oO0o0oO's avatar
Ooo0oO0o0oO committed
	}

	selectedInvoker = invoker.doSelectInvoker(lb, invocation, invokers, invoked)
	if sticky {
		invoker.stickyInvoker = selectedInvoker
	}
	return selectedInvoker
}

func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
	protocol.TryRefreshBlackList()
	if len(invokers) == 1 {
		if invokers[0].IsAvailable() {
			return invokers[0]
		}
		protocol.SetInvokerUnhealthyStatus(invokers[0])
Xin.Zh's avatar
Xin.Zh committed
		logger.Errorf("the invokers of %s is nil. ", invokers[0].GetURL().ServiceKey())
		return nil
vito.he's avatar
vito.he committed
	selectedInvoker := lb.Select(invokers, invocation)
LaurenceLiZhixin's avatar
LaurenceLiZhixin committed
	//judge if the selected Invoker is invoked and available
Ooo0oO0o0oO's avatar
Ooo0oO0o0oO committed
	if (!selectedInvoker.IsAvailable() && invoker.availablecheck) || isInvoked(selectedInvoker, invoked) {
		protocol.SetInvokerUnhealthyStatus(selectedInvoker)
		otherInvokers := getOtherInvokers(invokers, selectedInvoker)
		for i := 0; i < 3; i++ {
			if len(otherInvokers) == 0 {
				// no other ivk to reselect, return to fallback
LaurenceLiZhixin's avatar
LaurenceLiZhixin committed
				break
			}
			reselectedInvoker := lb.Select(otherInvokers, invocation)
			if isInvoked(reselectedInvoker, invoked) {
				otherInvokers = getOtherInvokers(otherInvokers, reselectedInvoker)
				continue
			}
			if !reselectedInvoker.IsAvailable() {
				logger.Infof("the invoker of %s is not available, maybe some network error happened or the server is shutdown.",
Xin.Zh's avatar
Xin.Zh committed
					invoker.GetURL().Ip)
				protocol.SetInvokerUnhealthyStatus(reselectedInvoker)
				otherInvokers = getOtherInvokers(otherInvokers, reselectedInvoker)
LaurenceLiZhixin's avatar
LaurenceLiZhixin committed
			return reselectedInvoker
LaurenceLiZhixin's avatar
LaurenceLiZhixin committed
	} else {
		return selectedInvoker
Xin.Zh's avatar
Xin.Zh committed
	logger.Errorf("all %d invokers is unavailable for %s.", len(invokers), selectedInvoker.GetURL().String())
LaurenceLiZhixin's avatar
LaurenceLiZhixin committed
	return nil
}

func isInvoked(selectedInvoker protocol.Invoker, invoked []protocol.Invoker) bool {
	for _, i := range invoked {
		if i == selectedInvoker {
			return true

func getLoadBalance(invoker protocol.Invoker, invocation protocol.Invocation) cluster.LoadBalance {
Xin.Zh's avatar
Xin.Zh committed
	url := invoker.GetURL()

	methodName := invocation.MethodName()
	//Get the service loadbalance config
	lb := url.GetParam(constant.LOADBALANCE_KEY, constant.DEFAULT_LOADBALANCE)

	//Get the service method loadbalance config if have
	if v := url.GetMethodParam(methodName, constant.LOADBALANCE_KEY, ""); len(v) > 0 {
		lb = v
	}
	return extension.GetLoadbalance(lb)
}
func getOtherInvokers(invokers []protocol.Invoker, invoker protocol.Invoker) []protocol.Invoker {
	otherInvokers := make([]protocol.Invoker, 0)
	for _, i := range invokers {
		if i != invoker {
			otherInvokers = append(otherInvokers, i)
	return otherInvokers