Fork me on GitHub

Dubbo路由层之LeastActiveLoadBalance

前言

之前介绍了负载均衡器之RandomLoadBalanceRoundRobinLoadBalance,今天分析一下LeastActiveLoadBalance

LeastActiveLoadBalance

最少活跃调用数负载均衡,根据文档描述:

  • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
  • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

举个栗子说明一下流程:比如有三个服务{a,b,c},每个服务都有一个活跃计数,初始化为0。a处理请求时,计数器+1为1,请求处理完后计数器-1为0。如果此时同时b,c也在处理请求,但是b,c的处理慢,计数器分别为1和2,那么此时a的活跃计数器最少为0,新的请求来时将由a来处理。

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    int length = invokers.size(); // 提供者数量
    int leastActive = -1; // 提供者中的最小活跃数
    int leastCount = 0; // 相同最小活跃数的数量
    int[] leastIndexs = new int[length]; // 相同最小活跃数的索引
    int totalWeight = 0; // 总权重
    int firstWeight = 0; // 第一个权重,用于比较权重是否相同
    boolean sameWeight = true; // 权重是否都相同
    for (int i = 0; i < length; i++) {
        Invoker<T> invoker = invokers.get(i);
        int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive(); // 活跃数
        int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT); // 权重
        if (leastActive == -1 || active < leastActive) { // 发现最小活跃数重新开始
            leastActive = active; // 记录当前最小活跃数
            leastCount = 1; // 重置相同最小活跃数的个数
            leastIndexs[0] = i; // 重置最小活跃数索引
            totalWeight = weight; // 重置总权重
            firstWeight = weight; // 记录第一个权重
            sameWeight = true; // 重置标识
        } else if (active == leastActive) { // 累计相同最小活跃数
            leastIndexs[leastCount++] = i; // 记录索引
            totalWeight += weight; // 累计总权重
            // 判断所有权重是否相同
            if (sameWeight && i > 0
                    && weight != firstWeight) {
                sameWeight = false;
            }
        }
    }
    // assert(leastCount > 0)
    if (leastCount == 1) {
        // 如果相同最小活跃数的数量只有1个,直接返回
        return invokers.get(leastIndexs[0]);
    }
    if (!sameWeight && totalWeight > 0) {
        // 如果权重不相同且权重大于0,则按总权重数获取一个随机值
        int offsetWeight = random.nextInt(totalWeight);
        // 根据权重随机返回一个invoker
        for (int i = 0; i < leastCount; i++) {
            int leastIndex = leastIndexs[i];
            offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
            if (offsetWeight <= 0)
                return invokers.get(leastIndex);
        }
    }
    // 如果权重相同或权重为0则随机获取一个
    return invokers.get(leastIndexs[random.nextInt(leastCount)]);
}

源码中的这个算法注释还是很详细的,鄙人就简单的翻译为中文方便观察… 这个算法过程还是比较明显的,大致过程分为两种步骤:

  1. 记录活跃数相关信息(最小活跃数,最小活跃数数量,相同活跃数索引,权重是否相同)
  2. 根据活跃数信息获取invokers。
    • 只有一个最小活跃数数量,直接返回该invoker
    • 权重相同,则根据最小活跃数数量随机获取invoker
    • 权重不相同,则根据权重获取invoker(此算法与RoundRobinLoadBalance中的权重获取有异曲同工之妙)

还有一个问题:那么何时修改活跃数呢?

答案是在com.alibaba.dubbo.rpc.filter.ActiveLimitFilter中。

我们可以看到ActiveLimitFilter中是如何拦截的,代码片段如下:

public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    // 省略部分代码...

    try {
        long begin = System.currentTimeMillis();
        RpcStatus.beginCount(url, methodName); // 活跃数+1
        try {
            Result result = invoker.invoke(invocation);
            RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true); // 活跃数-1
            return result;
        } catch (RuntimeException t) {
            RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);
            throw t;
        }
    } finally {
        // 省略部分代码
    }
}

可见,正如算法介绍那样,调用前活跃数+1,调用完成后活跃数-1。

结束语

大概最小活跃数就是这样的,算法是不是很简单~

-------------本文结束,感谢您的阅读-------------
贵在坚持,如果您觉得本文还不错,不妨打赏一下~
0%