dubbo源码分析-负载均衡算法

本文深入探讨一致性哈希算法的核心实现及其在负载均衡中的应用,包括虚拟节点分配、选择器函数以及权重随机与轮询两种路由策略的详细解释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一致性hash(consistent hash)

核心代码:

private Invoker<T> sekectForKey(long hash) {
            Invoker<T> invoker;
            Long key = hash;
            if (!virtualInvokers.containsKey(key)) {
                SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key);
                if (tailMap.isEmpty()) {
                    key = virtualInvokers.firstKey();
                } else {
                    key = tailMap.firstKey();
                }
            }
            invoker = virtualInvokers.get(key);
            return invoker;
        }

其中virtualInvokers是一棵红黑树,tailMap方法会返回所有hash值大于key的节点,然后取第一个节点即可。其中key值是根据调用的参数md5后再hash计算得出的:

public Invoker<T> select(Invocation invocation) {
            String key = toKey(invocation.getArguments());
            byte[] digest = md5(key);
            Invoker<T> invoker = sekectForKey(hash(digest, 0));
            return invoker;
        }
随机:(Random)

 protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        int length = invokers.size(); // 总个数
        int totalWeight = 0; // 总权重
        boolean sameWeight = true; // 权重是否都一样
        for (int i = 0; i < length; i++) {
            int weight = getWeight(invokers.get(i), invocation);
            totalWeight += weight; // 累计总权重
            if (sameWeight && i > 0
                    && weight != getWeight(invokers.get(i - 1), invocation)) {
                sameWeight = false; // 计算所有权重是否一样
            }
        }
        if (totalWeight > 0 && ! sameWeight) {
            // 如果权重不相同且权重大于0则按总权重数随机
            int offset = random.nextInt(totalWeight);
            // 并确定随机值落在哪个片断上
            for (int i = 0; i < length; i++) {
                offset -= getWeight(invokers.get(i), invocation);
                if (offset < 0) {
                    return invokers.get(i);
                }
            }
        }
        // 如果权重相同或权重为0则均等随机
        return invokers.get(random.nextInt(length));
    }
可以看到是按照权重来路由的,生成一个随机数,看这个随机数落在哪个invoker的权重区间上,那么就会调用哪个invoker。如果权重大,那么执行的机会就会多些。

轮询:(RoundRobin 

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
        int length = invokers.size(); // 总个数
        int maxWeight = 0; // 最大权重
        int minWeight = Integer.MAX_VALUE; // 最小权重
        for (int i = 0; i < length; i++) {
            int weight = getWeight(invokers.get(i), invocation);
            maxWeight = Math.max(maxWeight, weight); // 累计最大权重
            minWeight = Math.min(minWeight, weight); // 累计最小权重
        }
        if (maxWeight > 0 && minWeight < maxWeight) { // 权重不一样
            AtomicPositiveInteger weightSequence = weightSequences.get(key);
            if (weightSequence == null) {
                weightSequences.putIfAbsent(key, new AtomicPositiveInteger());
                weightSequence = weightSequences.get(key);
            }
            List<Invoker<?>> listInvoker = weightInvokers.get(key);
            if (listInvoker == null) {
            	weightInvokers.putIfAbsent(key, new ArrayList<Invoker<?>>());
            	listInvoker = weightInvokers.get(key);
            }
            synchronized(listInvoker){
	            if(listInvoker.isEmpty()){
	            	 // 重新构建
	            	 int currentWeight = weightSequence.getAndIncrement() % maxWeight;
	            	 for (Invoker<T> invoker : invokers) { // 筛选权重值大于等于当前权重基数的Invoker
	                     if (getWeight(invoker, invocation) >= currentWeight) {
	                    	 listInvoker.add(invoker);
	                     }
	                 }
	            }
	            return invokers.remove(0);
            }
        }
        AtomicPositiveInteger sequence = sequences.get(key);
        if (sequence == null) {
            sequences.putIfAbsent(key, new AtomicPositiveInteger());
            sequence = sequences.get(key);
        }
        // 取模轮循
        return invokers.get(sequence.getAndIncrement() % length);
    }
上面是修改后的代码,原来的代码有bug。逻辑很简单:基础权重从0开始逐渐增大到最大的权重,计算所有大于基础权重的invoker,并且依次调用。比如说有两个invoker:I1,I2的权重分别是权重4,7。那么I1的权重大于0,1,2,3基础权重,那么会被调用4次,而I2的权重大于0,1,2,3,4,5,6基础权重,所以会被调用7次,这样I1和I2被调用的次数之比就是4:7。


### Dubbo源码分析 #### 负载均衡器选择 Invoker 的详细逻辑 在 Dubbo 框架中,负载均衡器的核心作用是在多个可用的服务提供者之间分配请求流量。具体来说,在负载均衡器选择 `Invoker` 的过程中,Dubbo 首先会获取当前服务的所有可用提供者列表,并通过指定的负载均衡策略来决定最终调用哪个具体的 `Invoker` 实例[^1]。 以下是负载均衡器的主要工作流程: - **获取候选 Provider 列表**:从注册中心拉取所有在线的服务提供方地址。 - **过滤不可用实例**:移除那些因网络异常或其他原因而无法正常工作的服务实例。 - **执行负载均衡算法**:根据配置的负载均衡策略(如随机、轮询或一致性哈希),计算并返回目标 `Invoker`。 #### 配置管理机制解析 Dubbo 支持多种方式加载配置信息,其中一种常见的方式是通过 ZooKeeper 作为配置中心。为了确保系统的高可靠性和灵活性,Dubbo 提供了一套完善的配置优先级规则: 1. **本地配置覆盖远程配置**:如果同一参数既定义在本地也存在远端,则以本地为准[^2]。 2. **默认值补充缺失字段**:当某些必要选项未被显式声明时,默认设置会被自动填充进去。 这种设计不仅简化了运维操作难度,还增强了应对突发状况的能力——即使连接不上外部存储库也能维持基本功能运转。 #### 请求处理链路剖析 当客户端发起一次 RPC 调用后,该请求经过一系列处理器层层传递直至抵达实际业务方法之前经历了复杂的转换过程。以下列举几个关键环节及其职责所在: - **解码阶段 (DecodeHandler)** :负责将字节流还原成 Java 对象形式以便后续步骤能够理解其含义; - **头部交换协议适配层(HeaderExchangeHandler)** :主要完成消息头部分的数据组装拆分任务; - **核心协议实现类(DubboProtocol.requestHandler)** :承担着最为繁重的工作量,包括但不限于序列化反序列化控制以及线程池调度安排等等[^3]. 以上各组件紧密协作共同构建起了完整的通信桥梁结构图谱如下所示: ```mermaid sequenceDiagram participant Client as 客户端 participant NettyServer as Netty服务器 participant DecodeHandler as 解码处理器 participant HeaderExchangeHandler as 头部交换处理器 participant RequestHandler as 请求处理器 Client->>NettyServer: 发送数据包 activate NettyServer Note over NettyServer: 接收到原始二进制数据\n触发回调函数 NettyServer->>DecodeHandler: 执行decode()方法 activate DecodeHandler DecodeHandler-->>NettyServer: 返回Java对象表示的消息体 deactivate DecodeHandler NettyServer->>HeaderExchangeHandler: 继续向下游转发已解析好的Request实体 activate HeaderExchangeHandler HeaderExchangeHandler->>RequestHandler: 委托给requestHandler进一步加工 activate RequestHandler ... 更深层次的具体事务逻辑 ... end ``` #### 动态上下线通知机制探讨 对于动态调整集群规模场景下的优雅停机需求而言,Dubbo 设计了一个简单有效的解决方案即主动注销机制。每当某个 Provider 准备退出运行环境前都会提前告知 Registry 自己即将消失的事实;随后 Consumer 端订阅到这一变更事件之后便会及时更新内部缓存状态从而避免继续尝试访问已经失效的目标资源[^4]。 --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值