dubbo负载均衡代码分析3(加权轮询策略)

本文详细解析了Dubbo框架中的加权轮询算法实现原理,通过实例代码演示了权重如何影响服务调用的频率,并提供了源码级别的解读。

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

接上篇https://my.oschina.net/u/146130/blog/1573288

加权轮询,我第一次没理解,个人觉得不好理解。于是先仿照源码抽象出逻辑模型,代码如下:

    public static void main(String[] args) {
        //存储调用的方法和总的调用次数的map
         final ConcurrentMap<String, AtomicInteger> sequences = new ConcurrentHashMap<String, AtomicInteger>();
        //统计invoker 被调用次数map
         final ConcurrentMap<String, AtomicInteger> result = new ConcurrentHashMap<String, AtomicInteger>();
        //模拟方法三个invoker 分别为 a,b,c
        List<String> invokes=new ArrayList<String>(3);
        invokes.add("a");
        invokes.add("b");
        invokes.add("c");
        //存储invoker和权重的对应map
        final LinkedHashMap<String, AtomicInteger> invokerToWeightMap = new LinkedHashMap<String,AtomicInteger>();
         for(int i=0;i<21;i++){
             //每次调用都把模拟的权重重新放入
             invokerToWeightMap.put("a",new AtomicInteger(3));
             invokerToWeightMap.put("b",new AtomicInteger(6));
             invokerToWeightMap.put("c",new AtomicInteger(9));
             select(invokes,invokerToWeightMap,sequences,result);
         }
        //打印调用结果统计
         for(Map.Entry<String, AtomicInteger> r : result.entrySet()){
             System.out.println(r.getKey()+"被调用:"+r.getValue()+"次");
         }
        }

    private  static void  select(List<String> invokes,
                                 LinkedHashMap<String, AtomicInteger> invokerToWeightMap,
                                 ConcurrentMap<String, AtomicInteger> sequences,
                                 ConcurrentMap<String, AtomicInteger> result){
       //假设调用servcie.hello方法
        AtomicInteger sequence = sequences.get("service.hello");
        if (sequence == null) {
            //默认调用次数为0
            sequences.putIfAbsent("servcie.hello", new AtomicInteger(0));
            sequence = sequences.get("servcie.hello");
        }
        //调用次数+1
        int currentSequence = sequence.getAndIncrement();
        System.out.print("currentSequence:" + currentSequence);
        int maxWeight=9;//最大权重
        int minWeight=3;//最小权重
        int weightSum=18;//总权重
        if (maxWeight > 0 && minWeight < maxWeight) { // 走权重不一样逻辑。
            int mod = currentSequence % weightSum;
            System.out.print(" mod:" + mod);
            for (int i = 0; i < maxWeight; i++) {
                for (Map.Entry<String, AtomicInteger> each : invokerToWeightMap.entrySet()) {
                    final String k = each.getKey();
                    final AtomicInteger v = each.getValue();
                    if (mod == 0 && v.intValue() > 0) {
                        System.out.println(" selected:"+k);
                        AtomicInteger count = result.get(k);
                        if (count == null) {
                            result.putIfAbsent(k, new AtomicInteger(1));
                        }else{
                            count.incrementAndGet();
                        }
                        return;
                    }
                    if (v.intValue() > 0) {
                        v.decrementAndGet();
                        mod--;
                    }
                }
            }
        }

    }

输出结果,分析:

currentSequence:0 mod:0 selected:a
currentSequence:1 mod:1 selected:b
currentSequence:2 mod:2 selected:c
currentSequence:3 mod:3 selected:a
currentSequence:4 mod:4 selected:b
currentSequence:5 mod:5 selected:c
currentSequence:6 mod:6 selected:a
currentSequence:7 mod:7 selected:b
currentSequence:8 mod:8 selected:c
//前9次调用一直是简单轮询
currentSequence:9 mod:9 selected:b
currentSequence:10 mod:10 selected:c

currentSequence:11 mod:11 selected:b
currentSequence:12 mod:12 selected:c

currentSequence:13 mod:13 selected:b
currentSequence:14 mod:14 selected:c
//10到16次调用只在b c间轮询
currentSequence:15 mod:15 selected:c
currentSequence:16 mod:16 selected:c
currentSequence:17 mod:17 selected:c
//17到19次调用只调c
currentSequence:18 mod:0 selected:a
currentSequence:19 mod:1 selected:b
currentSequence:20 mod:2 selected:c
//由于mod取值归零,20到21次新一轮的轮询
最后调用总结:
a被调用:4次
b被调用:7次
c被调用:10次

体现出加权轮询,这就是duboo的加权轮询算法。

理解上面的代码,再看源代码,就容易理解很多。
源码如下:

 public static final String NAME = "roundrobin";

    private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();

    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; // 最小权重
        final LinkedHashMap<Invoker<T>, IntegerWrapper> invokerToWeightMap = new LinkedHashMap<Invoker<T>, IntegerWrapper>();
        int weightSum = 0;
        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 (weight > 0) {
                invokerToWeightMap.put(invokers.get(i), new IntegerWrapper(weight));
                weightSum += weight;
            }
        }
        AtomicPositiveInteger sequence = sequences.get(key);
        if (sequence == null) {
            sequences.putIfAbsent(key, new AtomicPositiveInteger());
            sequence = sequences.get(key);
        }
        //总的调用次数
        int currentSequence = sequence.getAndIncrement();
        if (maxWeight > 0 && minWeight < maxWeight) { // 权重不一样
            //取模操作,保证mod值域在[0,weightSum)
            int mod = currentSequence % weightSum;
            for (int i = 0; i < maxWeight; i++) {//maxWeight选最大权重,保证加上子循环
               // 即 maxWeight*(invoker个数)>mod 值。这个mod就可以减到0
                for (Map.Entry<Invoker<T>, IntegerWrapper> each : invokerToWeightMap.entrySet()) {
                    final Invoker<T> k = each.getKey();
                    final IntegerWrapper v = each.getValue();
                    //在一次选择过程后(mod--,权重--,mod==0结束),选下一个权重大于0的
                    if (mod == 0 && v.getValue() > 0) {
                        return k;
                    }
                    if (v.getValue() > 0) {
                        //基于mod,来说之前选过的,权重-1.mod--
                        v.decrement();
                        mod--;
                    }
                }
            }
        }
        // 权重一样   取模轮循 简单轮询
        return invokers.get(currentSequence % length);
    }

    private static final class IntegerWrapper {
        private int value;

        public IntegerWrapper(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public void decrement() {
            this.value--;
        }
    }

我理解,这个算法的思想,就是让权重小的候选者,通过-1 总是很快较早的不参与某个轮次的选择。

转载于:https://my.oschina.net/u/146130/blog/1574628

<think>好的,我现在需要帮助用户了解如何配置和使用Dubbo负载均衡策略。用户的问题是关于Dubbo负载均衡策略的配置和使用教程,我需要根据提供的引用信息来构建一个清晰的回答。 首先,我要确定用户的需求。用户可能是一个开发者,正在使用Dubbo框架构建分布式服务,需要了解不同的负载均衡策略以及如何配置它们。用户可能希望知道有哪些可用的策略,每种策略的特点,以及具体的配置方法。 接下来,我需要回顾提供的引用内容。引用1提到RandomLoadBalance是Dubbo的缺省实现,引用2详细描述了四种负载均衡策略:随机、轮询、最少活跃调用和一致性哈希。引用3和4展示了如何在代码中使用注解配置负载均衡,比如@DubboReference中的loadbalance参数,或者在XML配置中使用<dubbo:service>或<dubbo:reference>标签。引用5给出了一个服务实现的例子,虽然主要关注服务发布,但可能对配置的位置有帮助。 我需要组织这些信息,分步骤说明配置方法。首先介绍四种策略,每种策略的特点和适用场景,然后说明配置方式,包括注解和XML配置。此外,需要注意用户可能对如何选择策略有疑问,所以可能需要给出选择建议。 可能的问题点:是否覆盖了所有配置方式?比如注解和XML。是否提到了如何修改一致性哈希的参数,如虚拟节点数量?根据引用2,是的,需要提到hash.arguments和hash.nodes的配置。 还需要确保回答结构清晰,分步骤说明,帮助用户逐步完成配置。同时,生成的相关问题需要与主题相关,比如策略的区别、性能比较、生产环境推荐等。 现在,我需要将信息整合成一个结构化的回答,包括策略介绍、配置方法(代码示例)、配置位置,以及选择建议。同时,生成相关问题,帮助用户进一步了解。</think>### Dubbo负载均衡策略配置使用教程 #### 一、负载均衡策略类型 Dubbo提供4种内置策略,通过权重分配或算法实现流量调度: 1. **随机策略(random)** - 特点:按权重概率随机选择节点,调用量越大分布越均匀[^2] - 公式:$$ P(i) = \frac{weight_i}{\sum_{j=1}^n weight_j} $$ - 适用场景:常规分布式系统,服务节点性能差异较小 2. **轮询策略(roundrobin)** - 特点:按权重比例轮流分配请求,存在慢节点请求堆积风险[^2] - 调度算法: ```python current = (current + 1) % len(providers) ``` 3. **最少活跃数(leastactive)** - 特点:优先选择未完成请求数最少的节点,自动规避故障节点[^2] - 计算公式:$$ selected = \arg\min_{1≤i≤n}(active_i) $$ 4. **一致性哈希(consistenthash)** - 特点:相同参数请求固定路由到同一节点,默认使用160个虚拟节点[^2] - 配置参数: ```xml <dubbo:parameter key="hash.arguments" value="0,1"/> <dubbo:parameter key="hash.nodes" value="320"/> ``` #### 二、配置方式 **方式1:注解配置(Spring Boot)** ```java // 服务消费者配置 @DubboReference( loadbalance = "leastactive", // 策略类型 parameters = {"hash.nodes","320"} // 哈希策略专用参数 ) private UserService userService; ``` **方式2:XML配置** ```xml <!-- 服务提供方默认策略 --> <dubbo:service interface="com.example.Service" loadbalance="roundrobin"/> <!-- 服务消费方指定策略 --> <dubbo:reference id="userService" interface="com.example.UserService" loadbalance="consistenthash"> <dubbo:parameter key="hash.arguments" value="0"/> </dubbo:reference> ``` #### 三、配置生效位置 | 配置位置 | 作用范围 | 优先级 | |----------------|-------------|-------| | 服务提供方 | 全局默认策略 | 低 | | 服务消费方 | 指定服务生效 | 中 | | 方法级别配置 | 精确到接口方法| 高 | #### 四、策略选择建议 1. 服务节点配置差异大时优先使用**加权随机** 2. 需要会话保持场景使用**一致性哈希** 3. 节点响应时间波动较大时采用**最少活跃数** 4. 测试环境可使用轮询便于观察流量分布
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值