Dubbo的提供者发布服务是一个比较复杂的过程。概括起来说,主要有以下几步:
1.解析配置
2.导出服务
解析服务的过程略过。本文主要分析导出服务的过程,其中有一些比较重要的类,它们之间通过代理模式和装饰器模式,巧妙组合在一起,实现了Dubbo的提供者的功能,本文通过画图来解释它们。
导出服务又可以分为以下几步:
生成实现类的包裹对象即代理对象
假设我们定义了一个远程调用的接口,那么在提供者端就需要一个实现类来实现这个接口。同时为了实现监控,回调等功能,当请求到达时,不能简单根据类名和方法名去通过反射调用实现类。而是需要在此基础上对类进行包装,同时生成一系列代理对象。此外,Dubbo还通过责任链模式,实现了对请求的拦截处理链。概括起来,这个过程生成的类的关系图如下所示
最里层的ref代表接口的实现类,它是由用户开发的。从内到外,是Dubbo框架自动进行的一层层的包装。下面介绍一下其中比较重要的类或概念
1)Wrapper,是利用javassist字节码工具生成的ref的动态代理类,它不仅对ref实现的接口的方法进行了包装,同时也包装了ref的实现类的各种属性,方法的元数据等。
2)AbstractProxyInvoker是对Wrapper的包装,它是Invoker接口的默认实现类,在这里实际上是AbstractProxyInvoker类的一个抽象匿名子类。因为Invoker在拦截处理链中代表实现类的执行者,所以需要将Wrapper再包装为一个Invoker。值得注意的是AbstractProxyInvoker的invoke方法和doInvoker方法。
首先看doInvoke方法
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
这里的T泛型代表ref的接口,proxy就是ref,可以看出先生成了上文说的Wrapper包裹类,之后生成了AbstractProxyInvoker的抽象子类。这里的doInvoke方法就是通过wrapper去实际调用到ref里的方法实现了。
再看invoke方法
@Override
public Result invoke(Invocation invocation) throws RpcException {
try {
Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
CompletableFuture<Object> future = wrapWithFuture(value);
CompletableFuture<AppResponse> appResponseFuture = future.handle((obj, t) -> {
AppResponse result = new AppResponse();
if (t != null) {
if (t instanceof CompletionException) {
result.setException(t.getCause());
} else {
result.setException(t);
}
} else {
result.setValue(obj);
}
return result;
});
return new AsyncRpcResult(appResponseFuture, invocation);
} catch (InvocationTargetException e) {
if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {
logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
}
return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
} catch (Throwable e) {
throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
这个方法的第一行就是调用doInvoker方法。然后获取到结果之后,就将这个结果通过CompletableFuture进行异步化。通过这里我们看出,在框架层面Dubbo已经做到了完全的异步化,但是如果用户的方法是同步方法的话,这里还是会阻塞。如果要做到完全的异步化,可以将远程接口的返回类型定义为CompletableFuture。这样可以做到从框架到应用的完全异步化,在某些场景可以进一步提升性能。
2)DelegateProviderMetaDataInvoker只是简单包装,同时保存了服务的配置元信息。
3)InvokeDelegate是对DelegateProviderMetaDataInvoker的进一步封装,同时保存了注册的服务URL对象。
4)往外的各个Filter就是为了实现各种框架的必需功能或可选功能而嵌套的拦截链了。当请求进来时,会从EchoFilter开始向下穿越各个filter和代理对象,直至进入用户自己的实现类。其中可能开启某些功能时请求会在中途直接返回,而不继续向下。比如如果是回声测试,就会在EchoFilter就直接返回。
服务导出者类
这些类实现的接口是Exporter,代表了一个被导出的服务(即可以被远程调用的接口服务)。
DubboExporter代表是由Dubbo协议导出;ListenerExporterWrapper的功能是集成了一个ExporterListener列表,在导出时进行回调。
绑定服务端的类
在完成了形式上的导出服务之后,最重要的一步就是绑定服务端,也就是对外界开放接收网络连接,并且接收请求的能力。Dubbo底层默认是基于Netty的,所以最里层是NettyServer,它封装了通过Netty建立一个服务端的功能,外层是HeaderExchangeServer,它主要是封装了一些端到端连接的功能,比如长连接的空闲检测,在Dubbo框架层面管理连接等。DubboProtocolServer是进一步封装。
连接或消息处理类
在上图中,最里层的MultiMessageHandler并不是代表绑定服务端的类。而是一个连接或消息处理类。因此因此这一组类
ExchangeHandlerAdapter里层的其实是ExchangeHandlerAdapter这个类的抽象匿名子类,这个类只有一个方法,方法签名为
public CompletableFuture<Object> reply(ExchangeChannel channel, Object msg) throws RemotingException
它的实现类
@Override
public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
if (!(message instanceof Invocation)) {
throw new RemotingException(channel, "Unsupported request: "
+ (message == null ? null : (message.getClass().getName() + ": " + message))
+ ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
}
Invocation inv = (Invocation) message;
Invoker<?> invoker = getInvoker(channel, inv);
// need to consider backward-compatibility if it's a callback
// 省略次要代码
RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
Result result = invoker.invoke(inv);
return result.thenApply(Function.identity());
}
可见这个方法内部就是通过参数去获取Invoker对象,进而调用实现类。这里获取到的实现类,就是上文的包裹了EchoFilter的Invoker实例。同时这里reply方法返回类型是CompletableFuture,这是因为前面介绍的AbstractProxyInvoker的invoke方法对调用结果进行了异步化。
HeaderExchangeHandler主要还是管理连接等。
DecodeHandler主要是对请求进行解码。
AllChannelHandler是主要负责线程管理。
HeartbeatHandler主要负责心跳检测。
MultiMessageHandler主要负责将Dubbo内部定义的多条消息的聚合消息进行拆分处理。
总结
本文简单介绍了Dubbo提供者端发布服务时的一些重要的类及其组合关系。在阅读源码,梳理流程时,抓住这些关键的类,可以有助于理解。