Dubbo 负载均衡与集群容错(十一)

负载均衡

Dubbo支持的负载均衡有如下策略:默认是随机

  • 权重随机(random),实现类RandomLoadBalance
  • 权重轮询(roundrobin),实现类RoundRobinLoadBalance
  • 最少活跃(leastactive)负载策略,实现类LeastActiveLoadBalance
  • 一致性hash(consistenthash)  实现类ConsistentHashLoadBalance

在AbstractClusterInvoker#invoke => AbstractClusterInvoker#initLoadBalance

    protected LoadBalance initLoadBalance(List<Invoker<T>> invokers, Invocation invocation) {
        if (CollectionUtils.isNotEmpty(invokers)) {
            //从url通过key "loadbalance" 取不到值,就取默认random随机策略
            return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(RpcUtils.getMethodName(invocation), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
        } else {
            return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
        }
    }

FailoverClusterInvoker.doInvoke =>  

Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);  =>

Invoker<T> invoker = doSelect(loadbalance, invocation, invokers, selected) =>

Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);

到这边就进入了具体的负载策略逻辑了,下面以最小活跃数来介绍

    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        // Number of invokers  总个数
        int length = invokers.size();
        // The least active value of all invokers
        int leastActive = -1;  // 当前最小活跃数值
        // The number of invokers having the same least active value (leastActive)
        int leastCount = 0;   // 相同最小活跃数的个数
        // The index of invokers having the same least active value (leastActive)
        int[] leastIndexes = new int[length];   // 相同最小活跃数的下标
        // the weight of every invokers
        int[] weights = new int[length];
        // The sum of the warmup weights of all the least active invokes
        int totalWeight = 0;  // 总权重
        // The weight of the first least active invoke
        int firstWeight = 0;   // 第一个权重,用于于计算是否相同
        // Every least active invoker has the same weight value?
        boolean sameWeight = true;    // 是否所有权重相同


        // Filter out all the least active invokers
        for (int i = 0; i < length; i++) {
            Invoker<T> invoker = invokers.get(i);
            // Get the active number of the invoke
            int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
            // Get the weight of the invoke configuration. The default value is 100.
            int afterWarmup = getWeight(invoker, invocation);
            // save for later use
            weights[i] = afterWarmup;
            // If it is the first invoker or the active number of the invoker is less than the current least active number
            if (leastActive == -1 || active < leastActive) { // 每次循环到的invoker 得到的最小连接数 小于超过之前invoke的最小连接数
                // Reset the active number of the current invoker to the least active number
                leastActive = active;  //把最小的连接数赋值给leastActive
                // Reset the number of least active invokers
                leastCount = 1; // 当前相同的最小连接数reset为1个
                // Put the first least active invoker first in leastIndexs
                leastIndexes[0] = i;
                // Reset totalWeight
                totalWeight = afterWarmup;
                // Record the weight the first least active invoker
                firstWeight = afterWarmup;
                // Each invoke has the same weight (only one invoker here)
                sameWeight = true;
                // If current invoker's active value equals with leaseActive, then accumulating.
            } else if (active == leastActive) { // 发现当前的invoke最小连接数 与 目前最小连接数相同
                // Record the index of the least active invoker in leastIndexs order
                leastIndexes[leastCount++] = i;   // 最小连接数就+1     并保存这个invoker的下标到leastIndexes
                // Accumulate the total weight of the least active invoker
                totalWeight += afterWarmup;
                // If every invoker has the same weight?
                if (sameWeight && i > 0
                        && afterWarmup != firstWeight) {
                    sameWeight = false;
                }
            }
        }
        // Choose an invoker from all the least active invokers
        if (leastCount == 1) {  //  相同最小连接数只有一个的话   直接返回该下标的invoker
            // If we got exactly one invoker having the least active value, return this invoker directly.
            return invokers.get(leastIndexes[0]);
        }
        if (!sameWeight && totalWeight > 0) {
            // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on 
            // totalWeight.
            int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
            // Return a invoker based on the random value.
            for (int i = 0; i < leastCount; i++) {
                int leastIndex = leastIndexes[i];
                offsetWeight -= weights[leastIndex];
                if (offsetWeight < 0) {
                    return invokers.get(leastIndex);
                }
            }
        }
        // If all invokers have the same weight value or totalWeight=0, return evenly.
        // 如果权重相同或权重为0则均等随机
        return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
    }

其他的负载均衡策略相对简单,不过多介绍,读者可以网上查阅相关资料。另外关于加权轮询算法可以参看这篇文章:https://cloud.tencent.com/developer/article/1109584

 

集群容错

这是来自官网上的关于集群容错的示意图

 

 

下面对这几种集群容错模式的简单介绍,默认使用

Failfast Cluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

Failsafe Cluster

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

Failback Cluster

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

Forking Cluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

FailoverClusterInvoker(FailoverCluster,dubbo默认策略)

失败后自动选择其他服务提供者进行重试,重试次数由retries属性设置,< dubbo:reference retries = “2”/>设置,默认为2,代表重试2次,最多执行3次。

AvailableClusterInvoker

策略:选择集群第一个可用的服务提供者。
缺点:相当于服务的主备,但同时只有一个服务提供者承载流量,并没有使用集群的负载均衡机制。

 

这边以一个简单的AvailableClusterInvoker作为一个简单的例子来介绍一下,

AbstractClusterInvoker#invoke -> AvailableClusterInvoker#doInvoke

很简单 就是只要发现有一个invoker可以使用  就选择该invoker

public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        for (Invoker<T> invoker : invokers) {
            if (invoker.isAvailable()) {
                return invoker.invoke(invocation);
            }
        }
        throw new RpcException("No provider available in " + invokers);
    }
posted @ 2022-02-10 10:23  gaojy  阅读(16)  评论(0编辑  收藏  举报