Dubbo服务负载均衡原理

简介: 该文章主要介绍了Dubbo服务负载均衡的原理,包括Dubbo中负载均衡的实现位置、为什么需要负载均衡机制、Dubbo支持的负载均衡算法以及随机负载均衡策略的源码分析。

负载均衡是rpc框架必须具备的一个能力,在Dubbo中负载均衡是在服务消费者这边实现的,也叫做客户端负载均衡。

为什么需要负载均衡机制呢?

当服务提供者集群部署的时候,服务消费方每次调用都必须选择其中一台服务提供者进行​远程rpc调用。本文将分析Dubbo​的负载均衡算法。

Dubbo 支持哪些负载均衡算法

1、加权随机(默认)

2、加权轮训

问题:

存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

3、一致性hash

特点:相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

4、加权最少活跃数

最少活跃数 相同活跃数的随机,活跃数指调用前后计数差(调用的慢,调用时间越长,计数差越大)。最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

源码分析

1、随机负载均衡策略


public class RandomLoadBalance extends AbstractLoadBalance {
   
   public static final String NAME = "random";/**
     * Select one invoker between a list using a random criteria
     * @param invokers List of possible invokers
     * @param url URL
     * @param invocation Invocation
     * @param <T>
     * @return The selected invoker
     */
    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
   
   
        // Number of invokers
        int length = invokers.size();
        // Every invoker has the same weight?
        boolean sameWeight = true;
        // the weight of every invokers
        int[] weights = new int[length];
        // the first invoker's weight
        int firstWeight = getWeight(invokers.get(0), invocation);
        weights[0] = firstWeight;
        // The sum of weights
        int totalWeight = firstWeight;
        //计算每个服务提供者的权重
        for (int i = 1; i < length; i++) {
   
   
            int weight = getWeight(invokers.get(i), invocation);
            // save for later use
            weights[i] = weight;
            // Sum
            totalWeight += weight;
            if (sameWeight && weight != firstWeight) {
   
   
                sameWeight = false;
            }
        }
        //通过线程安全的随机生成器生成随机
        if (totalWeight > 0 && !sameWeight) {
   
   
            // 根据总权重计算一个随机权重偏移量
            int offset = ThreadLocalRandom.current().nextInt(totalWeight);
            // Return a invoker based on the random value.
            for (int i = 0; i < length; i++) {
   
   
                //遍历每个权重值,得到随机偏移量对应的权重
                offset -= weights[i];
                if (offset < 0) {
   
   
                    return invokers.get(i);
                }
            }
        }
        // If all invokers have the same weight value or totalWeight=0, return evenly.
        return invokers.get(ThreadLocalRandom.current().nextInt(length));
    }
}
public class LeastActiveLoadBalance extends AbstractLoadBalance {
   
   public static final String NAME = "leastactive";
    @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 是找到活跃数最小的一个invoker
            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
                //只要找到了更小活跃数的 invoker 就重置计数
                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) {
   
   
            //这里是为了 找出并记录和最小活跃数相同的invoker
                // Record the index of the least active invoker in leastIndexes order
                leastIndexes[leastCount++] = i;
                // Accumulate the total weight of the least active invoker
                //累计 相同活跃数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) {
   
   
            // 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)]);
    }
}

总结:

先过滤出调用次数最少(最不活跃)的调用者数量,并计算这些调用者的权重和数量。如果只有一个调用程序,则直接使用该调用程序;如果有多个调用者并且权重不相同,则根据总权重随机;如果有多个调用者且权重相同,则将其随机调用。

负载均衡机制是分布式系统中必须掌握的一项技术,通过分析Dubbo的源码实现,我们知道了常用的负载均衡算法以及一些优化版本,为学习其他中间件负载均衡机制打下基础。

相关文章
|
2月前
|
存储 设计模式 缓存
OpenFeign集成Ribbon负载均衡-过滤和选择服务核心实现
该文章主要介绍了如何在OpenFeign中集成Ribbon以实现负载均衡,并详细分析了Ribbon中服务选择和服务过滤的核心实现过程。文章还涉及了Ribbon中负载均衡器(ILoadBalancer)和负载均衡策略(IRule)的初始化方式。
OpenFeign集成Ribbon负载均衡-过滤和选择服务核心实现
|
2月前
|
负载均衡 算法 调度
负载均衡原理分析与源码解读
负载均衡原理分析与源码解读
|
2月前
|
负载均衡 算法 微服务
基于gRPC的注册发现与负载均衡的原理和实战
基于gRPC的注册发现与负载均衡的原理和实战
|
2月前
|
JSON Dubbo Java
【Dubbo协议指南】揭秘高性能服务通信,选择最佳协议的终极攻略!
【8月更文挑战第24天】在分布式服务架构中,Apache Dubbo作为一款高性能的Java RPC框架,支持多种通信协议,包括Dubbo协议、HTTP协议及Hessian协议等。Dubbo协议是默认选择,采用NIO异步通讯,适用于高要求的内部服务通信。HTTP协议通用性强,利于跨语言调用;Hessian协议则在数据传输效率上有优势。选择合适协议需综合考虑性能需求、序列化方式、网络环境及安全性等因素。通过合理配置,可实现服务性能最优化及系统可靠性提升。
43 3
|
2月前
|
消息中间件 负载均衡 API
RocketMQ生产者负载均衡(轮询机制)核心原理
文章深入分析了RocketMQ生产者的负载均衡机制,特别是轮询机制的实现原理,揭示了如何通过`ThreadLocal`技术和消息队列的选播策略来确保消息在多个队列之间均衡发送,以及如何通过灵活的API支持自定义负载均衡策略。
|
2月前
|
负载均衡 Dubbo 应用服务中间件
Dubbo服务调用过程原理
该文章主要介绍了Dubbo服务调用过程的原理,包括服务调用的主要阶段和服务调用的具体步骤。
Dubbo服务调用过程原理
|
2月前
|
C# 开发者 Windows
勇敢迈出第一步:手把手教你如何在WPF开源项目中贡献你的第一行代码,从选择项目到提交PR的全过程解析与实战技巧分享
【8月更文挑战第31天】本文指导您如何在Windows Presentation Foundation(WPF)相关的开源项目中贡献代码。无论您是初学者还是有经验的开发者,参与这类项目都能加深对WPF框架的理解并拓展职业履历。文章推荐了一些适合入门的项目如MvvmLight和MahApps.Metro,并详细介绍了从选择项目、设置开发环境到提交代码的全过程。通过具体示例,如添加按钮点击事件处理程序,帮助您迈出第一步。此外,还强调了提交Pull Request时保持专业沟通的重要性。参与开源不仅能提升技能,还能促进社区交流。
32 0
|
2月前
|
存储 负载均衡 监控
自适应负载均衡算法原理和实现
自适应负载均衡算法原理和实现
|
2月前
|
负载均衡 Kubernetes 开发工具
k8s相关服务与负载均衡
k8s相关服务与负载均衡
33 0
|
2月前
|
负载均衡 网络协议 Linux
在Linux中,负载均衡的原理是什么?
在Linux中,负载均衡的原理是什么?
下一篇
无影云桌面