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。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值