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