dubbo源码分析第二十四篇一dubbo负载均衡一RandomLoadBalance一RoundRobinLoadBalance
原创
©著作权归作者所有:来自51CTO博客作者ren5201313的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
- RandomLoadBalance加权随机
- RoundRobinLoadBalance加权轮询
RandomLoadBalance加权随机
- 权重不同则加权随机
- 权重相同则直接随机
- 权重同最小活跃数一样,前10分钟预热阶段,权重从0到配置的weight随时间平滑增长
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size();
boolean sameWeight = true;
int[] weights = new int[length];
int firstWeight = getWeight(invokers.get(0), invocation);
weights[0] = firstWeight;
int totalWeight = firstWeight;
for (int i = 1; i < length; i++) {
获取权重
int weight = getWeight(invokers.get(i), invocation);
weights[i] = weight;
获取总权重
totalWeight += weight;
判断是否存在权重不同
if (sameWeight && weight != firstWeight) {
sameWeight = false;
}
}
存在加权不同
if (totalWeight > 0 && !sameWeight) {
int offset = ThreadLocalRandom.current().nextInt(totalWeight);
// 循环让 offset 数减去服务提供者权重值,当 offset 小于0时,返回相应的 Invoker。
// 举例说明一下,我们有 servers = [A, B, C],weights = [5, 3, 2],offset = 7。
// 第一次循环,offset - 5 = 2 > 0,即 offset > 5,
// 表明其不会落在服务器 A 对应的区间上。
// 第二次循环,offset - 3 = -1 < 0,即 5 < offset < 8,
// 表明其会落在服务器 B 对应的区间上
for (int i = 0; i < length; i++) {
offset -= weights[i];
if (offset < 0) {
return invokers.get(i);
}
}
}
加权全部相同
return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
RoundRobinLoadBalance加权轮询
轮询逻辑: 根据[权重]增加所有Invoker的[轮询值] ([轮询值] += [权重])
[轮询值]大于当前最大值,作为本次被选中Invoker
[轮询值] 减少所有Invoker[权重] ([轮询值] -= total[权重])
权重: weight 轮询值: current
| Invoker1
| Invoker2
| Invoker3
| 选择结果
|
weight
| 100
| 100
| 100
| |
current
| 0
| 0
| 0
| |
current第一轮开始
| 100
| 100
| 100
| |
current第一轮结果
| -200
| 100
| 100
| 选择Invoker1 轮询值 = 100-100*3
|
current第二轮开始
| -100
| 200
| 200
| |
current第二轮结果
| -100
| -100
| 200
| 选择Invoker2 200-100*3
|
current第三轮开始
| 0
| 0
| 300
| |
current第三轮结果
| 0
| 0
| 0
| 选择Invoker3 300-100*3
|
一个周期后,所有的Invoker的current[轮询值]基本回归,如果权重相同,回归至0
源码分析
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
获取当前调用方法的 加权轮询元信息(WeightedRoundRobin)
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
{methodWeightMap: key->当前方法,val-> 所有Invoker对应的轮询信息} {map,当前所有的invoker key -> 当前Invoker的唯一id ,val -> 当前的轮询信息 }
ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.computeIfAbsent(key, k -> new ConcurrentHashMap<>());
int totalWeight = 0;
long maxCurrent = Long.MIN_VALUE;
long now = System.currentTimeMillis();
Invoker<T> selectedInvoker = null;
WeightedRoundRobin selectedWRR = null;
所有的Invoker遍历处理
for (Invoker<T> invoker : invokers) {
获取当前invoker的唯一标识
String identifyString = invoker.getUrl().toIdentityString();
获取每一个Invoker的权重
int weight = getWeight(invoker, invocation);
给每一个轮询信息设置权重
WeightedRoundRobin weightedRoundRobin = map.computeIfAbsent(identifyString, k -> {
WeightedRoundRobin wrr = new WeightedRoundRobin();
wrr.setWeight(weight);
return wrr;
});
if (weight != weightedRoundRobin.getWeight()) {
weightedRoundRobin.setWeight(weight);
}
每次增加自己的当前轮询值为权重weight
long cur = weightedRoundRobin.increaseCurrent();
weightedRoundRobin.setLastUpdate(now);
找出当前轮询值最小的Invoker
if (cur > maxCurrent) {
maxCurrent = cur;
selectedInvoker = invoker;
selectedWRR = weightedRoundRobin;
}
totalWeight += weight;
}
if (invokers.size() != map.size()) {
如果一个WeightedRoundRobin 60秒都没有参与过for循环里的轮询处理,则移除该Invoker
就要借助methodWeightMap记忆轮询的信息,又清除了Invoker变动残存的轮询信息
map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
}
if (selectedInvoker != null) {
每次被选中后,当前的轮询值减去所有Invoker的轮询值 从而达到轮询效果
selectedWRR.sel(totalWeight);
return selectedInvoker;
}
永远不会走到,兜底逻辑
return invokers.get(0);
}