注:本文基于dubbo v2.6.1
当我们在调用某个服务提供者暴露出来的接口的时候,实际上调用的是dubbo框架帮我们生成的代理类,例如我们现在有个服务提供者暴露的接口
public interface IHelloProviderService {
String getName(Integer id);
}
我们在启动服务的时候,dubbo会帮我们生成一个Proxy代理类,我们可以看下这个代理类的样子(之前我们在《深度解析dubbo服务远程引用(创建Proxy流程)》这篇文章中末尾曾经说过,现在我们在把之前代理类的图拿过来)
注意: 这个$echo这个方法是dubbo框架回声方法,我们可以不用管他
我们调用getName方法的时候,实际上调用的就是这个proxy里面的getName方法,因为这个代理类实现了IHelloProviderService 接口,并重写了getName方法,我们可以看到方法里面是执行了handler.invoke()方法,然后method对象与我们传入的参数值 传了进去。
这个handler 其实就是封装了我们之前 服务引用的时候生成的 FailoverClusterInvoker 对象。
这里这个handler 还不是FailoverClusterInvoker对象, 而是InvokerInvocationHandler这个类在外面又包了一层,大约是这个样子的层级。
1. InvokerInvocationHandler
我们先来看下这个InvokerInvocationHandler 类,它实现了接口InvocationHandler,重写invoke方法,我们来看下源码:
public class InvokerInvocationHandler implements InvocationHandler {
private final Invoker<?> invoker;
public InvokerInvocationHandler(Invoker<?> handler) {
this.invoker = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取方法名
String methodName = method.getName();
//获取方法参数类型们
Class<?>[] parameterTypes = method.getParameterTypes();
// 就是这个方法是Object类型的方法的时候
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
/// 下面都是调用了些Object的方法
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
//组装rpcinvocation对象 执行调用
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
他这里就一个作用,就是把Object的一些方法过滤去,当调用Object方法的时候直接调用invoker自己对象的方法,就不用远程调用了。
接下来就是看看这个FailoverClusterInvoker 类了
2.FailoverClusterInvoker
FailoverClusterInvoker 这个类是干什么的呢,可以从类名看出来,这个是一个失败重试的类。
先看下继承关系
我们可以看到FailoverClusterInvoker 继承AbstractClusterInvoker,AbstractClusterInvoker这个抽象类其实还有很多子类,我们可以看到这些都是,
然后这个invoke方法是在父类里面的,然后由子类实现doInvoke方法。我们先来看下这个AbstractClusterInvoker 的invoke方法。
// 实现接口的invoker方法
@Override
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed(); // 检验是否销毁
LoadBalance loadbalance = null; //定义负载均衡
// binding attachments into invocation.
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {// 就是将一些公共的kv 设置到invocation中
((RpcInvocation) invocation).addAttachments(contextAttachments);
}
// 获取所有服务提供者的集合
List<Invoker<T>> invokers = list(invocation);
if (invokers != null && !invokers.isEmpty()) {
// invokers 不是空 根据dubbo spi 获取负载均衡策略 默认是random
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(RpcUtils.getMethodName(invocation), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
}
// 如果是异步调用,就设置调用编号
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
// 具体实现是由子类实现的
return doInvoke(invocation, invokers, loadbalance);
}
// 检测是否已经销毁
protected void checkWhetherDestroyed() {
if (destroyed.get()) {
throw new RpcException("Rpc cluster invoker for " + getInterface() + " on consumer " + NetUtils.getLocalHost()
+ " use dubbo version " + Version.getVersion()
+ " is now destroyed! Can not invoke any more.");
}
}
1.首先检查是否销毁
2.将上下文中的附加信息添加到invocation 中。
3.获取invoker集合,获取负载均衡策略(这个后面讲)
4.调用子类的doInvoke(invocation, invokers, loadbalance) 方法。
我们来看下 FailoverClusterInvoker 的doInvoke 方法。
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyinvokers = invokers;
checkInvokers(copyinvokers, invocation);// 检验参数
// 获取重试次数,如果没有设置,就是用默认2+1 加1 操作是本身要调用一次,然后如果失败了 再重试调用1次
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. 异常
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); // 获取所有的invokers
// check again
checkInvokers(copyinvokers, invocation);
}
// 选择一个invoker
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
invoked.add(invoker); // 添加到已经选择的列表中
// 将已经选择过的invoker列表设置到context中
RpcContext.getContext().setInvokers((List) invoked);
try {
// 调用 获得结果
Result result = invoker.invoke(invocation);
if (le != null && logger.isWarnEnabled()) {//重试过程中,将最后一次调用的异常信息以 warn 级别日志输出
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; // 其他性质的异常统一封装成RpcException
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
// 将提供者的地址添加到providers
providers.add(invoker.getUrl().getAddress());
}
}
// 最后抛出异常
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);
}
其实 FailoverClusterInvoker 的doInvoke 方法中就是失败重试的逻辑,
首先获取重试次数默认情况是调用一次,然后重试2次。
我们看for循环中,不是第一次的时候,要重新获取invoker列表,这里就是为了保证invoker列表是最新的,能够跟注册中心的保持一致。
接着就是调用select()方法,这个方法其实就是根据负载均衡算法获取一个invoker,然后将这个invoker放到一个调用过的列表中,然后将这个列表塞到context中,之后就是调用invoker的invoke方法,返回Result 结果。异常处理是如果是业务异常 直接抛出不再重试,其他异常接着重试。
这个负载均衡获取invoker咱们这里就先不讲了,这个后面会有文章。
其实我们获取的这个invoker 还不直接是DubboInvoker,而是经过一堆FilterInvoker包装过的。先调用到FilterInvoker,最后才到这个DubboInvoker,这dubboInvoker我们这里就不看了,可以自己研究一下,然后下面这两张流程图是服务消费者调用一次要经过的invoker。