dubbo源码分析第二十四篇一dubbo负载均衡一RandomLoadBalance一RoundRobinLoadBalance
  Ojuk44AdL5AF 2023年11月02日 43 0


文章目录

  • ​​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);
}


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  ehrZuhofWJiC   2024年04月26日   42   0   0 日志Java
Ojuk44AdL5AF