1. 拿到InvokeInvocationHandle后,调用invoke()方法
2.invoke方法中如果是object类的方法,则执行,然后调用MockClusterInvoker的invoke()方法
MockClusterInvoker中包含了
- 服务目录Directory--》RegistryDirectory
- 下一个invoker-->FailOverClusterInvoker
- 将方法名和参数封装成RpcInvocation
3.进入MockClusterInvoker(这个类的作用:进行服务降级)的Invoke()方法中
- 获得"mock"配置项,有多种配置方式
- 无mock,则调用下一个invoker的invoke()方法
- 如果mock值是以force开头,直接调用Mock Invoker执行本地mock逻辑
- 调用原Invoker,发起RPC调用,如果是业务异常则直接抛出,如果是非业务异常,则执行fail-mock逻辑,调用Mock Invoker执行本地逻辑
4.下一个Invoker是FailOverClusterInvoker(此类的作用:服务容错)失败重试
- 首先进入父类AbstractClusterInvoker的invoke方法中,模板方法
- 获得LoadBalance对象
4.1 怎么获得负载均衡对象呢?
- list(invocation)在服务RegistryDirectory中获得所有服务提供者Invoker集合
4.1.1 怎么在服务目录中服务提供者的所有Invoker集合呢?
- list方法在父类AbstractDirectory中,模板方法,然后doList(invocation)调用具体的策略(RegistryDirectory---doList()方法)
4.1.1.1 进入RegistryDirectory(里面包含url和所有Invoker的集合,方法和所有Invoker的两个map)的doList方法
- 拿到methodInvokerMap,这个map是方法和Invoker的映射,
首先根据:方法名.第一个参数获取所有Invoker,
如果为空,则使用方法名从map中获取,
如果为空,获取*所有的Invoker集合,
如果为空,则循环这个map集合,拿到第一个,返回
这样就获取到了所有的Invoker集合,返回上一步
4.1.1.2 根据服务目录获取到了所有的Invoker集合,接下来进行Route.route(List invokers)路由过滤
如果配置了条件路由ConditionRoute会进入ConditionRoute.route方法,进行正则匹配,得到剩下的Invoker集合
这样就获取到了过滤后的Invoker集合
4.1.2 用spi的方式,从方法上获取负载均衡策略,默认是随机
- 设置调用编号,若是异步调用,将名为id,值为原子自增的值,设置到invocation的attachment中
- 执行调用doInvoker(invocation,invokers,loadBalance)
5.进入FailOverClusterInvoker的doInvoker中
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyinvokers = invokers;
//检查copyinvokers即可用Invoker集合是否为空,如果为空,那么抛出异常
checkInvokers(copyinvokers, invocation);
//得到最大可调用次数:最大可重试次数+1,默认最大可重试次数Constants.DEFAULT_RETRIES = 2
int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}
// retry loop.
//保存最后一次调用的异常
RpcException le = null; // last exception.
//保存已经调用过的Invoker
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
//failover机制核心实现:如果出现调用失败,那么重试其他服务器
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.
// 重试时,进行重新选择,避免重试时invoker列表已发生变化.
// 注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
if (i > 0) {
checkWhetherDestroyed();
//根据Invocation调用信息从Directory中获取所有可用Invoker
copyinvokers = list(invocation);
// check again
//重新检查一下
checkInvokers(copyinvokers, invocation);
}
// 根据负载均衡机制从copyinvokers中选择一个Invoker
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
// 保存每次调用的Invoker
invoked.add(invoker);
// 设置已经调用的 Invoker 集合,到 Context 中
RpcContext.getContext().setInvokers((List) invoked);
try {
// RPC 调用得到 Result
Result result = invoker.invoke(invocation);
// 重试过程中,将最后一次调用的异常信息以 warn 级别日志输出
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + invocation.getMethodName()
+ " 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) {
// 其他性质的异常统一封装成RpcException
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
// 最大可调用次数用完还得到Result的话,抛出RpcException异常:重试了N次还是失败,并输出最后一次异常信息
throw new RpcException(le != null ? le.getCode() : 0, "Failed to invoke the method "
+ invocation.getMethodName() + " 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 != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
}