Dubbo负载均衡

dubbo的负载均衡主要是在服务发现时按照某种策略选择一个Provider给Consumer。一切都源于LoadBalance这个接口。

@SPI(RandomLoadBalance.NAME)
public interface LoadBalance 
{      
    @Adaptive("loadbalance")    
    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}

通过AbstractLoadBalance静态类包含一些常用的方法和select函数的实现:

public abstract class AbstractLoadBalance implements LoadBalance {

   static int calculateWarmupWeight(int uptime, int warmup, int weight) {
        int ww = (int) ((float) uptime / ((float) warmup / (float) weight));
        return ww < 1 ? 1 : (Math.min(ww, weight));
    }

    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        if (CollectionUtils.isEmpty(invokers)) {
            return null;
        }
        //如果invokers列表中只有一个Invoker,直接返回即可。
        if (invokers.size() == 1) {
            return invokers.get(0);
        }
        return doSelect(invokers, url, invocation);
    }

    protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);

    protected int getWeight(Invoker<?> invoker, Invocation invocation) {
        int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);
        if (weight > 0) {
            long timestamp = invoker.getUrl().getParameter(REMOTE_TIMESTAMP_KEY, 0L);
            if (timestamp > 0L) {
                int uptime = (int) (System.currentTimeMillis() - timestamp);
                int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);
                if (uptime > 0 && uptime < warmup) {
                    weight = calculateWarmupWeight(uptime, warmup, weight);
                }
            }
        }
        return Math.max(weight, 0);
    }

其中实现的selcet方法做了一个简单的判断,如果包含invoker的list里面只有一个时就直接返回,否则调doSelect()方法。

calculateWarmupWeight方法: 根据预热时间来计算weight,预热时间是指某个invoker提供服务的能力是随着时间慢慢增加的,在某个时间以后达到稳定值,计算的公式是(uptime/warmup)*weight。如果小于1的话就设置为1.

getWeight方法:获得某个invoker的权重,首先从url里面获取权重的配置值。

int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);

如果设置的weight大于0的话,获取到invoker的启动时间:

long timestamp = invoker.getUrl().getParameter(REMOTE_TIMESTAMP_KEY, 0L);

如果启动的话,再计算启动时间是否达到warmup的水平,如果没有的话就调用calculateWarmupWeight方法来计算权重。

int uptime = (int) (System.currentTimeMillis() - timestamp);
int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);
if (uptime > 0 && uptime < warmup)
 {    weight = calculateWarmupWeight(uptime, warmup, weight);}

dubbo中实现的负载均衡算法有基于权重随机算法(RandomLoadBalance),基于最少活跃调用数算法的 LeastActiveLoadBalance、基于 hash 一致性的 ConsistentHashLoadBalance,以及基于加权轮询算法的 RoundRobinLoadBalance.

  • RandomLoadBalance

假设有一组servers=[s0,s1,s2],它们的权重为[5,4,3],权重总和是12,[0,5]属于s0,[5,9]属于s1, [9,12]属于s2。然后通过产生随机数的方法就行,只要产生随机数的机制比较好,那么每次选择其中一个机器的概率等于其权重在权重总和的比率。

@Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        //invokers的数量
        int length = invokers.size();
        //设置一个标识符来判断是否所有的invoker都有相同的weight
        boolean sameWeight = true;
        //保存每一个invoker的权重
        int[] weights = new int[length];
        // 第一个invoker的权重
        int firstWeight = getWeight(invokers.get(0), invocation);
        weights[0] = firstWeight;
        // 权重的总和
        int totalWeight = firstWeight;
        for (int i = 1; i < length; i++) {
           //通过调用getInvoler来获取invoker的weight 
           int weight = getWeight(invokers.get(i), invocation);
            // save for later use
            weights[i] = weight;
            // Sum
            totalWeight += weight;
            //权重不一样时将sameWeight设置为false
            if (sameWeight && weight != firstWeight) {
                sameWeight = false;
            }
        }
        if (totalWeight > 0 && !sameWeight) {
            // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
            //随机产生一个0-totalWeight的数
            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));
    }

代码的基本逻辑是先看是否所有的invoker都有相同的权重,如果是的话随机返回一个就好。不是的话就先产生一个随机值,然后判断这个随机值所在的区间然后返回对用的invoker。

for (int i = 0; i < length; i++) {
                offset -= weights[i];
                if (offset < 0) {
                    return invokers.get(i);
                }
            }

比如权重[5,4,3],然后随机的数为6,则首先6-5=1>0,接着1-4<0,因此返回s1.

优点: 实现简单,当请求次数很多的情况下会表现的很高效。

  • RoundRobinLoadBalance

本网站发布的一切文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请联系邮箱:1194325527@qq.com处理

目录
×

给作者杯卡布奇诺

github