深度解析dubbo负载均衡之RoundRobinLoadBalance

本文基于dubbo v2.6.x
本篇我们将要分析dubbo负载均衡RoundRobinLoadBalance的源码,RoundRobinLoadBalance主要是以轮询的方式来选取invoker,我们直接看下源码:

public class RoundRobinLoadBalance extends AbstractLoadBalance {
    public static final String NAME = "roundrobin";
    
    private static int RECYCLE_PERIOD = 60000;
    
    protected static class WeightedRoundRobin {
        private int weight;// 权重值
        private AtomicLong current = new AtomicLong(0);
        private long lastUpdate;// 最后修改时间
        public int getWeight() {
            return weight;
        }
        public void setWeight(int weight) {
            this.weight = weight;
            current.set(0);// 设置成0
        }

        // 将权重值设置到atomiclong中
        public long increaseCurrent() {
            return current.addAndGet(weight);
        }
        public void sel(int total) {
            current.addAndGet(-1 * total);
        }
        public long getLastUpdate() {
            return lastUpdate;
        }
        public void setLastUpdate(long lastUpdate) {
            this.lastUpdate = lastUpdate;
        }
    }
    // 缓存   key =接口全类名.方法名      value = map<每个invoker的标识,WeightedRoundRobin >
    private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();



    private AtomicBoolean updateLock = new AtomicBoolean();
    
    /**
     * get invoker addr list cached for specified invocation
     * <p>
     * <b>for unit test only</b>
     * 
     * @param invokers
     * @param invocation
     * @return
     */
    protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> invokers, Invocation invocation) {
        String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
        Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);
        if (map != null) {
            return map.keySet();
        }
        return null;
    }
    // 进行选择
    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {

        /// 组装key = 接口全类名.方法名
        String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
        // 根据key 从缓存中获取
        ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.get(key);

        // 处理是空的情况,如果为空就创建 然后塞进去
        if (map == null) {
            methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<String, WeightedRoundRobin>());
            map = methodWeightMap.get(key);
        }



        int totalWeight = 0;
        //初始化max 为long最小值
        long maxCurrent = Long.MIN_VALUE;
        // 获取系统当前时间
        long now = System.currentTimeMillis();
        Invoker<T> selectedInvoker = null;
        WeightedRoundRobin selectedWRR = null;

        // 遍历invokers
        for (Invoker<T> invoker : invokers) {

            // 将url 转成 身份标识
            String identifyString = invoker.getUrl().toIdentityString();

            // 从map中获取
            WeightedRoundRobin weightedRoundRobin = map.get(identifyString);
            // 计算权重
            int weight = getWeight(invoker, invocation);
            if (weight < 0) {// 如果计算的权重小于0 , 设置成0
                weight = 0;
            }
            if (weightedRoundRobin == null) {// 没有获取到,说明没有缓存
                weightedRoundRobin = new WeightedRoundRobin();//创建WeightedRoundRobin对象
                weightedRoundRobin.setWeight(weight);// 设置权重
                // 将WeightedRoundRobin 对象塞到 map中
                map.putIfAbsent(identifyString, weightedRoundRobin);
                weightedRoundRobin = map.get(identifyString);
            }

            //如果当前计算的权重与之前缓存的比匹配就设置成当前权重
            if (weight != weightedRoundRobin.getWeight()) {
                //weight changed
                weightedRoundRobin.setWeight(weight);
            }
            // 将权重值设置到atomiclong 中
            long cur = weightedRoundRobin.increaseCurrent();

            // 设置最后修改时间
            weightedRoundRobin.setLastUpdate(now);


            // 如果当前权重 > max
            if (cur > maxCurrent) {
                maxCurrent = cur;// max 设置成赋值为当前权重
                selectedInvoker = invoker;
                selectedWRR = weightedRoundRobin;
            }
            // 总权重+ 当前权重
            totalWeight += weight;
        }


        // 没有在修改的 && 当前所有的invoker 列表大小 不等于 缓存里面的大小
        if (!updateLock.get() && invokers.size() != map.size()) {
            if (updateLock.compareAndSet(false, true)) {// cas 操作
                try {
                    // copy -> modify -> update reference
                    ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<String, WeightedRoundRobin>();
                    newMap.putAll(map);// 将map添加到 newMap中


                    Iterator<Entry<String, WeightedRoundRobin>> it = newMap.entrySet().iterator();
                    while (it.hasNext()) {
                        Entry<String, WeightedRoundRobin> item = it.next();
                        // 移除 1分钟没有改动过的
                        if (now - item.getValue().getLastUpdate() > RECYCLE_PERIOD) {
                            it.remove();
                        }
                    }// 替换成新的
                    methodWeightMap.put(key, newMap);
                } finally {
                    updateLock.set(false);
                }
            }
        }


        if (selectedInvoker != null) {

            // 将选中的那个weightedRoundRobin 里面的current设置成负的 totalWeight
            selectedWRR.sel(totalWeight);
            return selectedInvoker;// 返回选中的那个invoker
        }
        /// 最后就是选择第一个
        // should not happen here
        return invokers.get(0);
    }

}

RoundRobinLoadBalance也是继承AbstractLoadBalance实现doSelect方法。我们先看doSelect的实现,首先是 生成key =接口全类名.方法名,去缓存methodWeightMap 中获取key对应的值,这个methodWeightMap 是个map,然后key就是咱们上面说的那个,value是 一个map (key是服务提供这url的信息,value是WeightedRoundRobin 对象,这对象我们稍后再看),这里没有key对应的value的话,就会new ConcurrentHashMap<String, WeightedRoundRobin>()塞进去。
接着就是遍历invokers,将invoker里面url转成 一个标示identifyString,以这个identifyString 为key从上面获取的那个map中获取对应的WeightedRoundRobin对象。计算该invoker权重,如果是小于0的设置为0。如果获取的WeightedRoundRobin对象为空的话,创建这个对象,将invoker对应的权重放到这个对象中,将这个对象缓存到map中,如果当前的权重与之前WeightedRoundRobin对象中的权重不相等,就设置到这个对象中。long cur = weightedRoundRobin.increaseCurrent();这句其实就是将权重塞到AtomicLong对象中,设置对象的最后更新时间。
接下来这段代码,其实就是找最大权重的 invoker跟对应的weightedRoundRobin,maxCurrent初始值是long的最小值,然后当前这个权重大于maxCurrent 的时候,赋值给maxCurrent ,同时selectedInvoker 就是那个invoker,selectedWRR 就是对应的weightedRoundRobin,这样子一圈下来,最后这个selectedInvoker 就是最大权重那个。

 			// 如果当前权重 > max
            if (cur > maxCurrent) {
                maxCurrent = cur;// max 设置成赋值为当前权重
                selectedInvoker = invoker;
                selectedWRR = weightedRoundRobin;
            }

再往下走 ,其实就是当 invoker列表大小与 map大小不一致的时候,这个不一致,一般就是有服务提供者下线的时候会出现,这里其实就是移除那些超过1分钟没有被更新过的(其实就是1分钟invokers列表没有出现过的)缓存,这里是先new 了个map,然后将之前的塞进去,遍历新的,移除那些掉线的服务,最后将新的map赋值给来的map。

  // 没有在修改的 && 当前所有的invoker 列表大小 不等于 缓存里面的大小(说明还有没缓存进去的)
        if (!updateLock.get() && invokers.size() != map.size()) {
            if (updateLock.compareAndSet(false, true)) {// cas 操作
                try {
                    // copy -> modify -> update reference
                    ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<String, WeightedRoundRobin>();
                    newMap.putAll(map);// 将map添加到 newMap中


                    Iterator<Entry<String, WeightedRoundRobin>> it = newMap.entrySet().iterator();
                    while (it.hasNext()) {
                        Entry<String, WeightedRoundRobin> item = it.next();
                        // 移除 1分钟没有改动过的
                        if (now - item.getValue().getLastUpdate() > RECYCLE_PERIOD) {
                            it.remove();
                        }
                    }// 替换成新的
                    methodWeightMap.put(key, newMap);
                } finally {
                    updateLock.set(false);
                }
            }
        }

最后,如果选中的那个invoker 不是空,将选中的那个weightedRoundRobin 里面的current设置成负的 totalWeight,这个totalWeight 就是所有权重的和,这样到下一轮就轮不到这个invoker了,所有都被选中一次的话都变成负的了,再从负的里面找最大的,所有又被选中一次,然后都变成了正数,就是这样轮询的 。最后实在没有获取到的话就取第一个invoker。

       if (selectedInvoker != null) {

            // 将选中的那个weightedRoundRobin 里面的current设置成负的 totalWeight
            selectedWRR.sel(totalWeight);
            return selectedInvoker;// 返回选中的那个invoker
        }
        /// 最后就是选择第一个
        // should not happen here
        return invokers.get(0);
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

$码出未来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值