Dubbo的负载均衡策略剖析

1 Dubbo的负载均衡策略概述

Dubbo的负载均衡策略应用于服务消费方。当服务提供者是集群时,通过在消费方设置负载均衡策略,避免大量请求一直集中在其中的某一个或者某几个服务提供方机器上

Dubbo提供了多种负载均衡策略,默认为随机策略-Random LoadBalance,即每次随机调用一台服务提供者的服务。

负载均衡策略的核心方法是各负载均衡策略类的 doSelect() 方法,用于从服务提供者列表中选择其中一个来调用

调用该方法的地方为 AbstractLoadBalance#select() 方法。源码如下所示。

public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    if (CollectionUtils.isEmpty(invokers)) {
        return null;
    }
    if (invokers.size() == 1) {
        return invokers.get(0);
    }
    return doSelect(invokers, url, invocation);
}

2 源码剖析

下面将介绍一些主要的负载均衡策略,以及对其源码进行解读。

2.1 随机策略-Random LoadBalance

2.1.1 概述

要点:每次随机调用一台服务提供者的服务,并且可以设置不同服务提供者的权重。

使用:

<dubbo:reference ... loadbalance="random"/>

2.1.2 源码剖析

实现类是RandomLoadBalance,源码如下所示。

/**
 * This class select one provider from multiple providers randomly.
 * You can define weights for each provider:
 * If the weights are all the same then it will use random.nextInt(number of invokers).
 * If the weights are different then it will use random.nextInt(w1 + w2 + ... + wn)
 * Note that if the performance of the machine is better than others, you can set a larger weight.
 * If the performance is not so good, you can set a smaller weight.
 */
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    // Number of invokers
    int length = invokers.size();

    if (!needWeightLoadBalance(invokers, invocation)) {
        return invokers.get(ThreadLocalRandom.current().nextInt(length));
    }

    // Every invoker has the same weight?
    boolean sameWeight = true;
    // the maxWeight of every invoker, the minWeight = 0 or the maxWeight of the last invoker
    int[] weights = new int[length];
    // The sum of weights
    int totalWeight = 0;
    for (int i = 0; i < length; i++) {
        int weight = getWeight(invokers.get(i), invocation);
        // Sum
        totalWeight += weight;
        // save for later use
        weights[i] = totalWeight;
        if (sameWeight && totalWeight != weight * (i + 1)) {
            sameWeight = false;
        }
    }
    if (totalWeight > 0 && !sameWeight) {
        // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
        int offset = ThreadLocalRandom.current().nextInt(totalWeight);
        // Return an invoker based on the random value.
        if (length <= 4) {
            for (int i = 0; i < length; i++) {
                // 注:和dubbo老版本不一样的点是权重大的不一定会首先被选中,因为 weights[i] = totalWeight
                if (offset < weights[i]) {
                    return invokers.get(i);
                }
            }
        } else {
            int i = Arrays.binarySearch(weights, offset);
            if (i < 0) {
                i = -i - 1;
            } else {
                while (weights[i+1] == offset) {
                    i++;
                }
                i++;
            }
            return invokers.get(i);
        }
    }
    // If all invokers have the same weight value or totalWeight=0, return evenly.
    return invokers.get(ThreadLocalRandom.current().nextInt(length));
}

2.2 轮循策略-RoundRobin LoadBalance

2.2.1 概述

要点:轮循,按照公约后的权重设置轮循比率。当某台机器执行很慢时,将导致该机器上的请求积压。

2.2.2 源码剖析

实现类是RoundRobinLoadBalance,源码如下所示。

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    String key = invokers.get(0).getUrl().getServiceKey() + "." + RpcUtils.getMethodName(invocation);
    ConcurrentMap<String, WeightedRoundRobin> map = ConcurrentHashMapUtils.computeIfAbsent(methodWeightMap, key, k -> new ConcurrentHashMap<>());
    int totalWeight = 0;
    long maxCurrent = Long.MIN_VALUE;
    long now = System.currentTimeMillis();
    Invoker<T> selectedInvoker = null;
    WeightedRoundRobin selectedWRR = null;
    for (Invoker<T> invoker : invokers) {
        String identifyString = invoker.getUrl().toIdentityString();
        int weight = getWeight(invoker, invocation);
        WeightedRoundRobin weightedRoundRobin = ConcurrentHashMapUtils.computeIfAbsent(map, identifyString, k -> {
            WeightedRoundRobin wrr = new WeightedRoundRobin();
            wrr.setWeight(weight);
            return wrr;
        });

        if (weight != weightedRoundRobin.getWeight()) {
            //weight changed
            weightedRoundRobin.setWeight(weight);
        }
        long cur = weightedRoundRobin.increaseCurrent();
        weightedRoundRobin.setLastUpdate(now);
        if (cur > maxCurrent) {
            maxCurrent = cur;
            selectedInvoker = invoker;
            selectedWRR = weightedRoundRobin;
        }
        totalWeight += weight;
    }
    if (invokers.size() != map.size()) {
        map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
    }
    if (selectedInvoker != null) {
        selectedWRR.sel(totalWeight);
        return selectedInvoker;
    }
    // should not happen here
    return invokers.get(0);
}

2.3 最少活跃调用数策略-LeastActive LoadBalance

2.3.1 概述

要点:在每个服务提供者里维护一个活跃数计数器,用来记录当前同时处理请求的个数。路由选择时会选择该活跃数最小的机器进行调用。

2.3.2 源码剖析

实现类是LeastActiveLoadBalance,源码如下所示。

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 invokers
    int totalWeight = 0;
    // The weight of the first least active invoker
    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 invoker
        int active = RpcStatus.getStatus(invoker.getUrl(), RpcUtils.getMethodName(invocation)).getActive();
        // Get the weight of the invoker's 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) {
            // Reset the active number of the current invoker to the least active number
            leastActive = active;
            // Reset the number of least active invokers
            leastCount = 1;
            // Put the first least active invoker first in leastIndexes
            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) {
            // Record the index of the least active invoker in leastIndexes order
            leastIndexes[leastCount++] = i;
            // Accumulate the total weight of the least active invoker
            totalWeight += afterWarmup;
            // If every invoker has the same weight?
            if (sameWeight && afterWarmup != firstWeight) {
                sameWeight = false;
            }
        }
    }
    // Choose an invoker from all the least active invokers
    if (leastCount == 1) {
        // 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.
    return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
}

2.4 一致性Hash策略-ConsistentHash LoadBalance

2.4.1 概述

要点:相同参数的请求总是发到同一服务提供者。

2.4.2 源码剖析

实现类是ConsistentHashLoadBalance,源码如下所示。

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    String methodName = RpcUtils.getMethodName(invocation);
    String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
    // using the hashcode of list to compute the hash only pay attention to the elements in the list
    int invokersHashCode = invokers.hashCode();
    ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
    if (selector == null || selector.identityHashCode != invokersHashCode) {
        selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, invokersHashCode));
        selector = (ConsistentHashSelector<T>) selectors.get(key);
    }
    return selector.select(invocation);
}

3 自定义负载均衡策略

3.1 自定义LoadBalance

创建一个继承 AbstractLoadBalance 的类,并重写 doSelect() 方法。举例如下。

public class MyLoadBalance extends AbstractLoadBalance {
    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        Invoker invoker = null;
        
        // 自定义负载均衡算法,从invokers中选择一个Invoker
        
        return invoker;
    }
}

3.2 配置和使用

在 resources 目录下, 添加 META-INF/dubbo 目录, 继而添加 org.apache.dubbo.rpc.cluster.LoadBalance 文件。并将自定义的 LoadBalance 类配置到该文件中。

myLoadBalance=org.apache.dubbo.rpc.cluster.loadbalance.MyLoadBalance

然后在消费接口时指定使用自定义的策略。

<dubbo:reference ... loadbalance="myLoadBalance"/>

备注:Dubbo自带的负载均衡策略配置如下所示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/381843.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

函数及函数的定义

前言&#xff1a; 在之前介绍指针的时候&#xff0c;小编发现有些地方需要用函数&#xff0c;所以小编决定先带领大家学习函数&#xff0c;然后再学习指针。 函数是从英文function翻译过来的&#xff0c;其实function在英文中的意思就是函数&#xff0c;也是功能的意思&#xf…

【深度学习:MPT-30B】提高开源基础模型的标准

【深度学习&#xff1a;MPT-30B】提高开源基础模型的标准 MPT-30B家族MPT-30B (Base)MPT-30B-InstructMPT-30B-Chat使用 MosaicML Inference 部署 MPT-30B 模型通过 MosaicML 培训定制 MPT-30BLLM Foundry 下一步是什么&#xff1f; 附录致谢数据MPT-30B 8k 上下文窗口微调数据…

【OrangePi Zero2 智能家居】阿里云人脸识别方案

一、接入阿里云 二、C语言调用阿里云人脸识别接口 三、System V消息队列和POSIX 消息队列 一、接入阿里云 在之前树莓派的人脸识别方案采用了翔云平台的方案去1V1上传比对两张人脸比对&#xff0c;这种方案是可行&#xff0c;可 以继续采用。但为了接触更多了云平台方案&…

常用工具类-Collections

常用工具类-Collections 排序操作查找操作填充操作判断集合是否有交集不可变集合 java.util.Collections类是一个工具类&#xff0c;它包含了一些静态方法&#xff0c;用于操作集合&#xff08;如列表和映射&#xff09;。这个类主要用于创建不可修改的集合、填充集合、替换元素…

「优选算法刷题」:数青蛙

一、题目 给你一个字符串 croakOfFrogs&#xff0c;它表示不同青蛙发出的蛙鸣声&#xff08;字符串 "croak" &#xff09;的组合。由于同一时间可以有多只青蛙呱呱作响&#xff0c;所以 croakOfFrogs 中会混合多个 “croak” 。 请你返回模拟字符串中所有蛙鸣所需不…

如何启动若依框架

Mysql安装 一、下载 链接&#xff1a;https://pan.baidu.com/s/1s8-Y1ooaRtwP9KnmP3rxlQ?pwd1234 提取码&#xff1a;1234 二、安装(解压) 下载完成后我们得到的是一个压缩包&#xff0c;将其解压&#xff0c;我们就可以得到MySQL 5.7.24的软件本体了(就是一个文件夹)&…

网络原理(一)

&#x1f495;"Echo"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;网络原理(一) 一. 应用层 应用层是和程序员联系最密切的一层,对于应用层来说,程序员可以自定义应用层协议,应用层的协议一般要约定好以下两部分内容: 根据需求,明确要传输哪些信…

MySQL进阶45讲【19】幻读是什么,幻读会产生什么问题?

1 前言 在MySQL进阶45讲【3】事务隔离的恩恩怨怨这篇文章中&#xff0c;我们有提到过幻读的概念&#xff0c;为了更好地介绍幻读&#xff0c;我们先创建一个表&#xff0c;并添加一些数据&#xff0c;建表和初始化语句如下&#xff1a; CREATE TABLE t ( id int(11) NOTNULL,…

「C++ 类和对象篇 10」初始化列表

目录 一、什么是初始化列表&#xff1f; 二、为什么需要初始化列表&#xff1f; 三、初始化列表怎么使用&#xff1f; 3.1 在构造函数中使用初始化列表 3.2 注意 3.3 结论 3.4 应用场景 四、初始化列表的初始化顺序 五、另一种初始化成员变量的方法 【总结】 一、什么是初始化…

【深度学习】:滴滴出行-交通场景目标检测

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主&#xff0c;接实验技术指导1对1 有任…

单目深度估计任意未标记数据:释放大规模数据潜力 | 开源日报 No.166

LiheYoung/Depth-Anything Stars: 2.6k License: Apache-2.0 Depth-Anything 是一个释放大规模未标记数据力量的项目&#xff0c;可以对任意未标记数据进行单目深度估计。 该项目主要功能、关键特性和核心优势包括&#xff1a; 相对深度估计度量深度估计更好的深度条件控制网…

【Java八股面试系列】并发编程-并发关键字,线程池

目录 并发关键字 Synchronized synchronized最主要的三种使用方式&#xff1a; 具体使用&#xff1a;双重校验锁单例模式 synchronized 底层实现原理&#xff1f; synchronized锁的优化 偏向锁 轻量级锁 重量级锁 Mark Word 与 Monitor 之间的关系 总结 偏向锁、轻量…

阿里百秀移动端首页

技术选型 方案:采取响应式页面开发方案技术: bootstrap框架设计图∶设计图采用1280px设计尺寸 屏幕划分分析 屏幕缩放发现中屏幕和大屏幕布局是一致的。因此我们列定义为col-md-就可以了&#xff0c;md是大于等于970以上的屏幕缩放发现小屏幕布局发生变化&#xff0c;因此我…

ArcGIS学习(四)坐标系-1

ArcGIS学习(四)坐标系 大家平时在处理数据的时候肯定经常遇到坐标系相关的问题。最常见的就是同一个地区的两个数据,导入ArcGIS内却对不上;也肯定听到过坐标系相关的一些词语,比如地理坐标系投影坐标系、投影、WGS1984坐标、CGCS2000坐标系、火星坐标系、百度坐标系等。 …

架构(十二)动态Excel

一、引言 作者最近的平台项目需要生成excel&#xff0c;excel的导入导出是常用的功能&#xff0c;但是作者想做成动态的&#xff0c;不要固定模板&#xff0c;那就看看怎么实现。 二、后端 先捋一下原理&#xff0c;前后端的交互看起来是制定好的接口&#xff0c;其实根本上是…

算法学习——华为机考题库9(HJ56 - HJ63)

算法学习——华为机考题库9&#xff08;HJ56 - HJ63&#xff09; HJ56 完全数计算 描述 完全数&#xff08;Perfect number&#xff09;&#xff0c;又称完美数或完备数&#xff0c;是一些特殊的自然数。 它所有的真因子&#xff08;即除了自身以外的约数&#xff09;的和&…

基于idea解决springweb项目的Java文件无法执行问题

前言 上一篇文章的话介绍了spring以及创建spring项目&#xff0c;但是因为有宝子私聊我说创建的项目那个JAVA文件显示灰色还有一个红点&#xff0c;问我怎么解决下面我来简答的写一下怎么修改配置让他正常的运行 配置 原因好像是因为基于maven的JAVA项目构架&#xff0c;对应…

主干网络篇 | YOLOv5/v7 更换主干网络为 VGG13 / VGG16 / VGG19 | 对比实验必备

论文地址:https://arxiv.org/pdf/1409.1556.pdf 在这项工作中,我们研究了卷积网络深度对其在大规模图像识别环境中准确性的影响。我们的主要贡献是对使用非常小(33)卷积滤波器的架构的不断增加深度的网络进行了彻底评估,这表明通过将深度推进到16-19个权重层,可以在先前…

【漏洞复现】狮子鱼CMS某SQL注入漏洞

Nx01 产品简介 狮子鱼CMS&#xff08;Content Management System&#xff09;是一种网站管理系统&#xff0c;它旨在帮助用户更轻松地创建和管理网站。该系统拥有用户友好的界面和丰富的功能&#xff0c;包括页面管理、博客、新闻、产品展示等。通过简单直观的管理界面&#xf…