本文简单介绍 Dubbo 负载均衡策略中的轮询策略。
1
轮询负载均衡策略
Dubbo 中实现轮询策略的代码是:RoundRobinLoadBalance。这个策略和随机策略有一个比较大的差异在于,轮询策略需要知道上次是哪个实例被调用了,Dubbo 是记录了每个被调用的方法被调用的次数,因为只需要通过取余计算就可以得到这一次要调用的实例,不用直接记录上一次被调用的实例。
轮询的策略算法也是分为 2 种情况。
2
轮询策略的优缺点
3
RoundRobinLoadBalance 源码
public class RoundRobinLoadBalance extends AbstractLoadBalance {
public static final String NAME = "roundrobin";
// 记录所有提供服务的数据,<serviceKey+methodName, 调用次数>
private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
// 被调用实例数
int length = invokers.size(); // Number of invokers
// 最大权重值
int maxWeight = 0; // The maximum weight
// 最小权重值
int minWeight = Integer.MAX_VALUE; // The minimum weight
// 记录每个实例以及对应的权重
final LinkedHashMap<Invoker<T>, IntegerWrapper> invokerToWeightMap = new LinkedHashMap<Invoker<T>, IntegerWrapper>();
// 权重之和
int weightSum = 0;
// 遍历所有被调用实例
for (int i = 0; i < length; i++) {
// 获取对应实例的权重值
int weight = 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 > 0) {
// 只添加有权重的实例
invokerToWeightMap.put(invokers.get(i), new IntegerWrapper(weight));
weightSum += weight;
}
}
// 获取该服务的调用次数
AtomicPositiveInteger sequence = sequences.get(key);
if (sequence == null) {
// 没有调用记录则添加数据
sequences.putIfAbsent(key, new AtomicPositiveInteger());
sequence = sequences.get(key);
}
// 调用次数加一
int currentSequence = sequence.getAndIncrement();
// 实例有权重,则根据权重大小分配
if (maxWeight > 0 && minWeight < maxWeight) {
// 将调用次数 % 权重总数,得出偏移量 mod
int mod = currentSequence % weightSum;
// 遍历最大的权重值,
// 为什么会遍历它?
// 因为每一次循环就遍历所有的实例,一个实例最大的权重为 maxWeight,
// 最多遍历 maxWeight 次所有实例就可以找到想要的实例
for (int i = 0; i < maxWeight; i++) {
// 遍历所有的实例
for (Map.Entry<Invoker<T>, IntegerWrapper> each : invokerToWeightMap.entrySet()) {
final Invoker<T> k = each.getKey();
final IntegerWrapper v = each.getValue();
if (mod == 0 && v.getValue() > 0) {
// mod 为 0 表示选中了,但要满足该实例的权重大于 0
return k;
}
if (v.getValue() > 0) {
// 实例没选中,则权重减 1,相当于选中机会少了
v.decrement();
// 偏移量也减 1
mod--;
}
}
}
}
// 没有权重则 调用次数 % 实例数量 对应下标的实例返回
// Round robin
return invokers.get(currentSequence % length);
}
private static final class IntegerWrapper {
private int value;
public IntegerWrapper(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public void decrement() {
this.value--;
}
}
}
做个有梦想的程序猿