文章目录
FailoverClusterInvoker
- 重试会重新调用list(获取提供者集合外加路由)获取最新Invoker结果,因提供者可能有变动,重新获取本地缓存,确保监听变更后的RegisterDirectory能被及时感知
- 重试通过循环实现
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 = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}
// 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++) {
if (i > 0) {
checkWhetherDestroyed();
在进行重试前重新列举 Invoker,这样做的好处是,如果某个服务挂了, 通过调用 list 可得到最新可用的 Invoker 列表原因是register是监听zk的
copyInvokers = list(invocation);
check again
checkInvokers(copyInvokers, invocation);
}
通过负载均衡策略选择Invoker
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List) invoked);
try {
Result result = invoker.invoke(invocation);
return result;
} catch (RpcException e) {
if (e.isBiz()) {
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);
}
总结
- 假设配置timeout1秒,加上重试3次,一个接口就会出现3秒超时
- 注意此场景带来的雪崩效应