-----------------看过之前一致性哈希的可以跳过-----------------?
链接在此:Dubbo的几个负载均衡类--一致性哈希
消费者发起调用过程中涉及如下几步
1:接口调用,比如DemoService.demoMethod
2:InvokerInvocationHandler.invoker:消费端启动时,通过JavassistProxyFactory.getProxy反射获取代理类,之后服务调用就直接调用这个Handler
3:MigrationInvoker.invoke:Dubbo 发起调用非常重要的一步,如果失败了,通过这个invoker做切换
4:其他
5:FailoverClusterInvoker.invoke(目前我们使用的,实际在AbstractClusterInvoker里面invoke逻辑是固定的)
6:其他
@Override
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
// binding attachments into invocation.
Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
}
//如果设置了标签规则,则通过list方法过滤出来符合标签的几个invoker
List<Invoker<T>> invokers = list(invocation);
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
//请求负载并且做好灾备降级
return doInvoke(invocation, invokers, loadbalance);
}
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyInvokers = invokers;
checkInvokers(copyInvokers, invocation);
String methodName = RpcUtils.getMethodName(invocation);
int len = calculateInvokeTimes(methodName);
// retry loop.
RpcException le = null; // last exception.
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
for (int i = 0; i < len; i++) {
//Reselect before retry to avoid a change of candidate `invokers`.
//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
if (i > 0) {
checkWhetherDestroyed();
copyInvokers = list(invocation);
// check again
checkInvokers(copyInvokers, invocation);
}
//这里通过loadBalance做负载
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List) invoked);
try {
Result result = invoker.invoke(invocation);
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + methodName
+ " in the service " + getInterface().getName()
+ " was successful by the provider " + invoker.getUrl().getAddress()
+ ", but there have been failed providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ le.getMessage(), le);
}
return result;
} catch (RpcException e) {
if (e.isBiz()) { // biz exception.
throw e;
}
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
throw new RpcException(le.getCode(), "Failed to invoke the method "
+ methodName + " in the service " + getInterface().getName()
+ ". Tried " + len + " times of the providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ Version.getVersion() + ". Last error is: "
+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
}
-----------------看过之前一致性哈希的可以跳过-----------------?
源码如下,
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
// Number of invokers
int length = invokers.size();
// The least active value of all invokers
int leastActive = -1;
// The number of invokers having the same least active value (leastActive)
int leastCount = 0;
// The index of invokers having the same least active value (leastActive)
int[] leastIndexes = new int[length];
// the weight of every invokers
int[] weights = new int[length];
// The sum of the warmup weights of all the least active invokers
int totalWeight = 0;
// The weight of the first least active invoker
int firstWeight = 0;
// Every least active invoker has the same weight value?
boolean sameWeight = true;
// Filter out all the least active invokers
for (int i = 0; i < length; i++) {
Invoker<T> invoker = invokers.get(i);
// Get the active number of requests on the invoker
int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
// Get the weight of the invoker's configuration. The default value is 100.
int afterWarmup = getWeight(invoker, invocation);
// save for later use
weights[i] = afterWarmup;
// If it is the first invoker or the active number of the invoker is less than the current least active number
if (leastActive == -1 || active < leastActive) {
// Reset the active number of the current invoker to the least active number
leastActive = active;
// Reset the number of least active invokers
leastCount = 1;
// Put the first least active invoker first in leastIndexes
leastIndexes[0] = i;
// Reset totalWeight
totalWeight = afterWarmup;
// Record the weight the first least active invoker
firstWeight = afterWarmup;
// Each invoke has the same weight (only one invoker here)
sameWeight = true;
// If current invoker's active value equals with leaseActive, then accumulating.
} else if (active == leastActive) {
// Record the index of the least active invoker in leastIndexes order
leastIndexes[leastCount++] = i;
// Accumulate the total weight of the least active invoker
totalWeight += afterWarmup;
// If every invoker has the same weight?
if (sameWeight && afterWarmup != firstWeight) {
sameWeight = false;
}
}
}
// Choose an invoker from all the least active invokers
if (leastCount == 1) {
// If we got exactly one invoker having the least active value, return this invoker directly.
return invokers.get(leastIndexes[0]);
}
if (!sameWeight && totalWeight > 0) {
// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on
// totalWeight.
int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
// Return a invoker based on the random value.
for (int i = 0; i < leastCount; i++) {
int leastIndex = leastIndexes[i];
offsetWeight -= weights[leastIndex];
if (offsetWeight < 0) {
return invokers.get(leastIndex);
}
}
}
// If all invokers have the same weight value or totalWeight=0, return evenly.
return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
}
首先Dubbo通过RpcStatus记录每个接口的调用次数,记录的工具是一个AtomicInteger
方法getWeight是处理那些启动在10分钟内的服务,做一定的服务负载降级。超过十分钟,那么返回的负载都是100,如果小于1分钟那么就是1,1分钟到10分钟之间分别的是分钟数的平方值。
基本针对的就是我们在版本发布的时候,需要启动某个服务的所有实例,这时候越早启动的权重值越大,这么做的原因其实也很合理,越早启动的越稳定,包括各种缓存热点的加载,连接数/链接池的初始化等等
然后就开始比active的大小了,大概意思就是把当前最小active的往数组前面放
一轮遍历后的leastIndexs就是一个有序的递增数组,也就是第一个元素的active最小,所以会有下面一个if
if (leastCount == 1) {
// If we got exactly one invoker having the least active value, return this invoker directly.
return invokers.get(leastIndexes[0]);
}
但如果有多个最小的,那么就要看权重了。如果有权重不一样,就执行如下,意思就是在总权重范围内选一个随机值,然后看这个随机值能够到哪个,那么就用哪个。
if (!sameWeight && totalWeight > 0) {
// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on
// totalWeight.
int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
// Return a invoker based on the random value.
for (int i = 0; i < leastCount; i++) {
int leastIndex = leastIndexes[i];
offsetWeight -= weights[leastIndex];
if (offsetWeight < 0) {
return invokers.get(leastIndex);
}
}
}
如果权重一样,就随机选一个
return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
李梓萌:
[1,2,3]:出来的leastIndexes数组只有一个:[1,0,0],直接取第一个。(只走一次第一个if,后面两不符合条件)
[1,1,1]:出来的leastIndexes数组有三个:[1,1,1],如果权重再一样,随机取一个;如果权重不一样,3内随机一个权重值比如2,那么就选第三个(注意条件是:offsetWeight<0)(第一个1走的第一个if,后面两1,都是走的第二个if)
[2,1,2]:出来的leastIndexes数组有一个:[1,0,0],直接取第一个(走了两次第一个if,最后那个2不符合条件)
[2,2,1]:出来的leastIndexes数组有两个:[1,2,0],直接取第一个(先走了第一个if,再走第二个if,第三个1又走了第一个if)
[2,3,3,2,1]:出来的leastIndexes数组有两个:[1,2,0,0,0],直接取第一个(同上,中间3不符合条件)
总结:最小活跃数真的就会取最少活跃的那个。
不过我咋没找到这个active在调用后怎么递增的呢
这是我问“文文”的回答