本文简单介绍 Dubbo 负载均衡策略中的轮询策略。
1
轮询负载均衡策略
Dubbo 中实现轮询策略的代码是:RoundRobinLoadBalance。这个策略和随机策略有一个比较大的差异在于,轮询策略需要知道上次是哪个实例被调用了,Dubbo 是记录了每个被调用的方法被调用的次数,因为只需要通过取余计算就可以得到这一次要调用的实例,不用直接记录上一次被调用的实例。
轮询的策略算法也是分为 2 种情况。
被调用的服务的所有实例都没有权重
当所有实例都没有权重的时候,则通过 [调用总次数 + 1] % [实例数],就可以获取到这次要调用的实例
被调用的服务的实例的权重不一
举个例子来说比较形象一点,如下图所示,假设有 A - F 总共 6 个实例,分别是如下权重。最大的权重 maxWeight = 200,通过 [调用总次数 + 1] % [总的权重值] 得到这次要选中的偏移量 mod,实现算法是顺时针遍历实例,如果不是选中所在的实例,则实例的权重 减 1,mod 也减 1,一直循环到 mod 为 0时,所在的实例的权重不为 0 ,则选中实例。也就是说这个圆会一直变小,ABCDEF遍历,如果没选中,这 6 个实例的权重都减 1,这才保证权重大的选中的几率高。不像随机策略,随机策略就是直接定位偏移量 mod 的位置在哪个实例。
2
轮询策略的优缺点
优点:实现简单,易水平扩展,且比较均衡分发到所有实例
缺点:无法知道所有的实例的情况。
3
RoundRobinLoadBalance 源码
public classRoundRobinLoadBalanceextendsAbstractLoadBalance {
public static finalStringNAME="roundrobin";
// 记录所有提供服务的数据,
private finalConcurrentMapsequences=newConcurrentHashMap();
@Override
protected InvokerdoSelect(List> invokers,URL url,Invocation invocation) {
String key = invokers.get().getUrl().getServiceKey() +"."+ invocation.getMethodName();
// 被调用实例数
intlength = invokers.size();// Number of invokers
// 最大权重值
intmaxWeight =;// The maximum weight
// 最小权重值
intminWeight = Integer.MAX_VALUE;// The minimum weight
// 记录每个实例以及对应的权重
finalLinkedHashMap,IntegerWrapper> invokerToWeightMap =newLinkedHashMap,IntegerWrapper>();
// 权重之和
intweightSum =;
// 遍历所有被调用实例
for(inti =;i
// 获取对应实例的权重值
intweight = getWeight(invokers.get(i),invocation);
// 设置最大、最小权重值
maxWeight = Math.max(maxWeight,weight);// Choose the maximum weight
minWeight = Math.min(minWeight,weight);// Choose the minimum weight
if(weight >) {
// 只添加有权重的实例
invokerToWeightMap.put(invokers.get(i), newIntegerWrapper(weight));
weightSum += weight;
}
}
// 获取该服务的调用次数
AtomicPositiveInteger sequence =sequences.get(key);
if(sequence ==null) {
// 没有调用记录则添加数据
sequences.putIfAbsent(key, newAtomicPositiveInteger());
sequence =sequences.get(key);
}
// 调用次数加一
intcurrentSequence = sequence.getAndIncrement();
// 实例有权重,则根据权重大小分配
if(maxWeight >&& minWeight
// 将调用次数 % 权重总数,得出偏移量 mod
intmod = currentSequence % weightSum;
// 遍历最大的权重值,
// 为什么会遍历它?
// 因为每一次循环就遍历所有的实例,一个实例最大的权重为 maxWeight,
// 最多遍历 maxWeight 次所有实例就可以找到想要的实例
for(inti =;i
// 遍历所有的实例
for(Map.Entry,IntegerWrapper> each : invokerToWeightMap.entrySet()) {
finalInvoker k = each.getKey();
finalIntegerWrapper v = each.getValue();
if(mod ==&& v.getValue() >) {
// mod 为 0 表示选中了,但要满足该实例的权重大于 0
returnk;
}
if(v.getValue() >) {
// 实例没选中,则权重减 1,相当于选中机会少了
v.decrement();
// 偏移量也减 1
mod--;
}
}
}
}
// 没有权重则 调用次数 % 实例数量 对应下标的实例返回
// Round robin
returninvokers.get(currentSequence % length);
}
private static final classIntegerWrapper {
private intvalue;
publicIntegerWrapper(intvalue) {
this.value= value;
}
public intgetValue() {
returnvalue;
}
public voidsetValue(intvalue) {
this.value= value;
}
public voiddecrement() {
this.value--;
}
}
}
做个有梦想的程序猿
领取专属 10元无门槛券
私享最新 技术干货