深度剖析ApacheDubbo核心技术内幕
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.2 Dubbo服务提供方如何处理请求

我们先看看当消费端发起TCP链接并完成后,在接收消费端发来的请求时,服务提供方是如何处理的,其处理时序图如图3.6所示。

图3.6

让我们看看图3.6中的connected部分(步骤1~步骤11),消费端发起TCP链接并完成后,服务提供方的NettyServer的connected方法会被激活,该方法的执行是在Netty的I/O线程上执行的,为了可以及时释放I/O线程,Netty默认的线程模型为All,正如在第7章所介绍的,所有消息都派发到Dubbo内部的业务线程池,这些消息包括请求事件、响应事件、连接事件、断开事件、心跳事件等,这里对应的是AllChannelHandler类把I/O线程接收到的所有消息包装为ChannelEventRunnable任务并都投递到了线程池里。

线程池里的任务被执行后,最终会调用DubboProtocol的connected()方法,其代码如下:

其中,invoke()是一个通用方法,其代码如下:

其中,createInvocation()方法的代码如下:

在这里的URL内不包含Constants.ON_CONNECT_KEY,所以直接返回了null。

图3.6中的received部分(步骤12~步骤22)类似于connected,received事件被投递到线程池后进行异步处理。线程池任务被激活后调用了HeaderExchangeHandler的received()方法,其代码如下:

如果请求不需要响应结果则直接调用DubboProtocol的received()方法,否则执行handleRequest()方法,后者代码如下:

如果请求需要返回值则执行handleRequest()方法,其也是委托给DubboProtocol的reply()方法来执行的。如果执行结果已经完成,则直接将结果写回消费端,否则使用异步回调方式(避免当前线程被阻塞),等执行完毕并拿到结果后再把结果写回消费端。

通过图3.6可知,无论消费端是否需要执行结果,最终都是DubboProtocol类的reply()方法来执行具体的服务,下面我们看看reply()方法的代码:

代码7首先使用getInvoker()方法获取调用方法对应的DubboExporter对象导出的Invoker对象,具体代码如下。在3.1节中我们介绍了导出的DubboExporter对象会保存到exporterMap中,这里的getInvoker获取的是RegistryProtocol$InvokerDelegate:

代码8把对端的地址设置到上下文对象中。

代码9执行导出的Invoker的invoke()方法,这里有个调用链,经过调用链后最终调用了服务提供方启动时AbstractProxyInvoker代理类创建的invoke()方法,其调用时序如图3.7所示。

图3.7

图3.7显示的是调用DubboProtocol的reply()方法的情况,在调用InvokerDelegate的invoke()方法前会先经过Filter链(这里只列出来了Filter链中的一部分Filter),然后InvokerDelegate会调用服务提供方启动时AbstractProxyInvoker代理类的invoke()方法,其代码如下:

代码11获取上下文对象,代码11.1调用doInvoke()方法,并使用JavaAssist来执行本地服务,以便减少反射调用,这里我们再回顾一下doInvoke()方法的代码:

通过上面的代码可知,AbstractProxyInvoker的doInvoke()方法委托Wrapper类的invokeMethod执行具体逻辑,后者则通过调用服务提供方接口的实现类来执行本地服务,细节可参考2.5.4小节。

需要注意的是,步骤11.2返回的结果是区分情况的,如果返回值类型为CompletableFuture,或者如果是使用RpcContext.startAsync()开启异步执行的,则返回AsyncRpcResult对象,否则返回正常的RpcResult对象,我们在随后的11.2.1小节中会用到这些内容。

另外,如果代码10发现结果为AsyncRpcResult,则说明是服务提供方的异步执行,此时返回对应的CompletableFuture对象,如果返回的是正常的RpcResult结果,则使用CompletableFuture.completedFuture(result);把结果转换为CompletableFuture对象(即同步转异步),以便统一处理,在随后的11.2.1小节中我们也会讲到。