dubbo负载均衡包括4种,随机、轮询、最小活跃调用数、IP哈希
Dubbo负载均衡类图如下
Dubbo使用负载均衡方式很简单,只需要在服务调用者标签上配置loadbalance属性就行
@Reference(loadbalance = "random")
如果配置了权重,则invoker权重越大,执行的概率越大;如果没有配置权重,则随机选择invoker执行
@Reference(loadbalance = "roundrobin")
如果配置了权重,则根据权重轮询;否则直接通过方法调用次数取模
@Reference(loadbalance = "leastactive")
指的是当请求调用来临,有多个实例提供服务的时候,选择其中被调用活跃次数最少的实例来提供服务。通俗一点讲就是,当前有 3 个实例在提供服务,A 当前被 2 个服务调用,B 当前被 3 个服务调用,C 当前被 1 个服务调用,一个新的调用请求过来,会选择调用到 C 实例。
能动态的根据当前服务的调用情况,选择最小被调用的实例,调用越慢的机器,会接收到更少的请求。因为调用越慢的机器调用间隔越大,累积的请求越多
@Reference(loadbalance = "consistenthash")
相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
protected Invoker doSelect(List> invokers, URL url, Invocation invocation) {
// 获取所有的服务提供者invoker数量
int length = invokers.size();
// 初始化总权重为0
int totalWeight = 0;
// 默认权重都相等
boolean sameWeight = true;
int offset;
int i;
for(offset = 0; offset < length; ++offset) {
// 获取某个invoker的权重(可以在@Server注解上配置)
i = this.getWeight((Invoker)invokers.get(offset), invocation);
totalWeight += i;
if (sameWeight && offset > 0 && i != this.getWeight((Invoker)invokers.get(offset - 1), invocation)) {
// 如果任意相邻两个invoker权重不相等则把sameWeight设置为false
sameWeight = false;
}
}
// 下面是轮询权重算法
if (totalWeight > 0 && !sameWeight) {
// 从0-totalWeight中随机选取一个数
offset = this.random.nextInt(totalWeight);
for(i = 0; i < length; ++i) {
// 这个数减去该invoker的offset
offset -= this.getWeight((Invoker)invokers.get(i), invocation);
if (offset < 0) {
// 如果相减后的值<0则用该invoker,权重越大,相减后<0的概率越大
return (Invoker)invokers.get(i);
}
}
}
// 如果权重都相同或者权重算法相减后都>0则随机选一个
return (Invoker)invokers.get(this.random.nextInt(length));
}
protected Invoker doSelect(List> invokers, URL url, Invocation invocation) {
// 用全类名和方法名做key
String key = ((Invoker)invokers.get(0)).getUrl().getServiceKey() + "." + invocation.getMethodName();
// 获取invoker的数量
int length = invokers.size();
int maxWeight = 0;
int minWeight = 2147483647;
// 做一个invoker-weight map
LinkedHashMap, RoundRobinLoadBalance.IntegerWrapper> invokerToWeightMap = new LinkedHashMap();
int weightSum = 0;
int currentSequence;
for(int i = 0; i < length; ++i) {
// 当前invoker权重
currentSequence = this.getWeight((Invoker)invokers.get(i), invocation);
// 设置最大权重和最小权重
maxWeight = Math.max(maxWeight, currentSequence);
minWeight = Math.min(minWeight, currentSequence);
if (currentSequence > 0) {
// 如果权重>0则把invoker-weight放到map中
invokerToWeightMap.put(invokers.get(i), new RoundRobinLoadBalance.IntegerWrapper(currentSequence));
weightSum += currentSequence;
}
}
// 获取这个方法的执行次数,如果为空则初始化
AtomicPositiveInteger sequence = (AtomicPositiveInteger)this.sequences.get(key);
if (sequence == null) {
this.sequences.putIfAbsent(key, new AtomicPositiveInteger());
sequence = (AtomicPositiveInteger)this.sequences.get(key);
}
// 调用次数自增1
currentSequence = sequence.getAndIncrement();
// 如果有权重,则考虑权重因素进行选取
if (maxWeight > 0 && minWeight < maxWeight) {
int mod = currentSequence % weightSum;
for(int i = 0; i < maxWeight; ++i) {
Iterator i$ = invokerToWeightMap.entrySet().iterator();
while(i$.hasNext()) {
Entry, RoundRobinLoadBalance.IntegerWrapper> each = (Entry)i$.next();
Invoker k = (Invoker)each.getKey();
RoundRobinLoadBalance.IntegerWrapper v = (RoundRobinLoadBalance.IntegerWrapper)each.getValue();
if (mod == 0 && v.getValue() > 0) {
return k;
}
if (v.getValue() > 0) {
v.decrement();
--mod;
}
}
}
}
// 根据方法调用次数取模,一次次来
return (Invoker)invokers.get(currentSequence % length);
}
protected Invoker doSelect(List> invokers, URL url, Invocation invocation) {
// 获取invoker个数
int length = invokers.size();
// 最小活跃值
int leastActive = -1;
// 最小活跃数量
int leastCount = 0;
// 做一个数组,记录最小活跃invoker的下标
int[] leastIndexs = new int[length];
int totalWeight = 0;
int firstWeight = 0;
boolean sameWeight = true;
int offsetWeight;
int leastIndex;
for(offsetWeight = 0; offsetWeight < length; ++offsetWeight) {
Invoker invoker = (Invoker)invokers.get(offsetWeight);
// 获取该实例的活跃值
leastIndex = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), "weight", 100);
if (leastActive != -1 && leastIndex >= leastActive) {
if (leastIndex == leastActive) {
// 如果该invoker == 最小活跃度则把该invoker下标记录到leastIndexs中
leastIndexs[leastCount++] = offsetWeight;
totalWeight += weight;
if (sameWeight && offsetWeight > 0 && weight != firstWeight) {
sameWeight = false;
}
}
} else {
// 如果出现更小活跃度的invoker则重新设置最小活跃度的值
leastActive = leastIndex;
// 更新最小活跃度个数为1
leastCount = 1;
// 把该invoker下标记录到leastIndexs中
leastIndexs[0] = offsetWeight;
totalWeight = weight;
firstWeight = weight;
sameWeight = true;
}
}
if (leastCount == 1) {
// 如果最小活跃数量invoker个数为1则直接返回
return (Invoker)invokers.get(leastIndexs[0]);
} else {
// 如果有多个最小活跃数量的invoker
if (!sameWeight && totalWeight > 0) {
// 如果考虑权重则根据权重做随机
offsetWeight = this.random.nextInt(totalWeight);
for(int i = 0; i < leastCount; ++i) {
leastIndex = leastIndexs[i];
offsetWeight -= this.getWeight((Invoker)invokers.get(leastIndex), invocation);
if (offsetWeight <= 0) {
return (Invoker)invokers.get(leastIndex);
}
}
}
// 如果没有权重则从最小活跃度invoker中随机选择一个
return (Invoker)invokers.get(leastIndexs[this.random.nextInt(leastCount)]);
}
}
protected Invoker doSelect(List> invokers, URL url, Invocation invocation) {
// 用全类名和方法名做key
String key = ((Invoker)invokers.get(0)).getUrl().getServiceKey() + "." + invocation.getMethodName();
// 基于 invokers 集合,根据对象内存地址来计算定义哈希值
int identityHashCode = System.identityHashCode(invokers);
// 获取根据key获取本次执行的选择器
ConsistentHashLoadBalance.ConsistentHashSelector selector = (ConsistentHashLoadBalance.ConsistentHashSelector)this.selectors.get(key);
if (selector == null || selector.identityHashCode != identityHashCode) {
this.selectors.put(key, new ConsistentHashLoadBalance.ConsistentHashSelector(invokers, invocation.getMethodName(), identityHashCode));
selector = (ConsistentHashLoadBalance.ConsistentHashSelector)this.selectors.get(key);
}
return selector.select(invocation);
}
public Invoker select(Invocation invocation) {
// 根据方法参数做key
String key = this.toKey(invocation.getArguments());
// 加密
byte[] digest = this.md5(key);
// 选取执行的invoker
return this.selectForKey(this.hash(digest, 0));
}