前言
之前介绍了负载均衡器之RandomLoadBalance
和RoundRobinLoadBalance
,今天分析一下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)]);
}
源码中的这个算法注释还是很详细的,鄙人就简单的翻译为中文方便观察… 这个算法过程还是比较明显的,大致过程分为两种步骤:
- 记录活跃数相关信息(最小活跃数,最小活跃数数量,相同活跃数索引,权重是否相同)
- 根据活跃数信息获取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。
结束语
大概最小活跃数就是这样的,算法是不是很简单~