丛林背景

dubbo框架的核心就是远程调用。在设计上,作者对远程调用的实现进行了分层,一共是三层:business层、rpc层与remoting层。

  • business层,这一层有为RPC接口创建的动态代理对象
  • rpc层,这一层的核心为Invoker链
  • remoting层,封装底层通信细节,如果底层通信走netty4则这一层的核心为: NettyChannel、ChannelHandler链以及dubbo线程池

本文探秘dubbo远程调用中客户端和服务端的行为,咋们这篇不求甚解,只要在脑海中形成清晰的远程调用的流程图即可。


文章目录

  • 丛林背景
  • (一)客户端发起远程调用的过程
  • 1.1 business层解析
  • 1.2 rpc层解析
  • 1.2.1 DubboInvoker
  • 1.2.2 AsyncToSyncInvoker
  • 1.3 remoting层解析
  • 1.3.1 ExchangeClient
  • 1.3.2 NettyChannel
  • (二)客户端收到服务端响应的过程
  • 1.1 DefaultFuture
  • (三)服务端处理请求的过程
  • 3.1 remoting层分析
  • 3.1.1 AllChannelHandler
  • 3.1.2 HeaderExchangeHandler
  • 3.2 rpc层分析
  • 3.2.1 AbstractProxyInvoker


(一)客户端发起远程调用的过程

以下图示是我在客户端发起一次远程调用的客户端调用链截图,整个客户端的调用链比较清晰,三层的界限圈出来了,以下将对每一层进行分析。

dubboRpc远程调用如何熔断_远程调用

1.1 business层解析

RPC接口的动态代理对象持有了InvokerInvocationHandler对象。InvokerInvocationHandler对象持有了Invoker对象,并将请求委托给Invoker对象来处理。

dubboRpc远程调用如何熔断_rpc_02

1.2 rpc层解析

rpc层上,请求从MockClusterInvoker一直传递给DubboInvoker,可以把这一段看成一个变体的责任链。DubboInvoker位于invoker链的尾部,委托给remoting层来发送tcp请求。

dubboRpc远程调用如何熔断_远程调用_03

1.2.1 DubboInvoker

DubboInvoker 委托给ExchangeClient对象来发起请求,请求由此传递到了remoting层,ExchangeClient.request方法返回一个CompletableFuture对象,RPC层通过CompletableFuture对象可以获取到调用的结果。

dubboRpc远程调用如何熔断_远程调用_04

1.2.2 AsyncToSyncInvoker

AsyncToSyncInvoker是invoker链中的一个类,在invoker.invoke()方法调用返回之后,他会阻塞在

Future.get()方法中(Result实现了Future接口)直到收到一个响应结果才会接着往下执行。

所以当客户端的业务线程发起远程调用时就会阻塞在这个方法上,直到有响应结果。

dubboRpc远程调用如何熔断_远程调用_05

1.3 remoting层解析

remoting层封装底层通信的细节。这次客户端的远程调用请求最终委托给netty4的 NioSocketChannel.writeAndFlush方法。根据netty4的React线程池模型的实现,NioSocketChannel.writeAndFlush方法是会将这次写事件提交给netty的worker线程池来执行。

1.3.1 ExchangeClient

ExchangeClient 负责 RPC层与Remoting层的数据交互,RPC层的数据实体为Invocation,Remoting层的数据实体为Request与Response。在客户端调用过程中ExchangeClient 负责将Invocation封装为Request对象。

1.3.2 NettyChannel

NettyChannel持有一个netty4的NioSocketChannel对象,并调用它的writeAndFlush来发送请求。

dubboRpc远程调用如何熔断_客户端_06

(二)客户端收到服务端响应的过程

客户端的netty worker线程在收到服务端发来的tcp响应包后会封装为一个ChannelEventRunnable对象并提交给DubboClientHandler线程池来处理。ChannelEventRunnable任务就是调用DefaultFuture.complete方法并传入远程调用的结果。

dubboRpc远程调用如何熔断_dubboRpc远程调用如何熔断_07

1.1 DefaultFuture

DefaultFuture继承了CompletableFuture类,这个类在设计上是用来获取远程调用的结果。当DubboClientHandler线程调用DefaultFuture对象的comlete方法并传入远程调用的结果,则阻塞在future.get() 的业务线程就能拿到远程调用的结果,这次调用流程基本就走完了!

dubboRpc远程调用如何熔断_rpc_08

(三)服务端处理请求的过程

以下截图为服务端收到远程调用请求后的调用链,我红框圈出来的是一个channelHandler链,最下层的NettyServerHandler是一个适配器,适配了dubbo自家的ChannelHandler接口并实现了netty4的ChannelHandler接口,由NettyServerHandler这个适配器作为dubbo自家的ChannelHandler链的链首。这个ChannelHandler链的链尾是AllChannelHandler。

dubboRpc远程调用如何熔断_客户端_09


以下截图为dubbo线程池处理上述被提交的任务,并最终调用服务对象的目标方法的调用链截图。

dubboRpc远程调用如何熔断_dubboRpc远程调用如何熔断_10

3.1 remoting层分析
3.1.1 AllChannelHandler

AllChannelHandler作为channelFilter链的尾部,这个地位可想而知。如以下截图所示,他会将TCP请求封装为一个ChannelEventRunnable对象,并分发给dubbo线程池。

dubboRpc远程调用如何熔断_java_11

这边还是得额外拓展以下,根据dubbo.protocol.dispatcher的配置不同,可以有以下5种选择,由于我配置的dubbo.protocol.dispatcher=all(默认的配置) 所以这边对应的分发策略为AllChannelHandler,对应这个处理器,worker线程的连接建立事件、连接失效事件、发送数据事件、接受数据事件等都会提交给dubbo线程池来处理。

dubboRpc远程调用如何熔断_java_12

3.1.2 HeaderExchangeHandler

HeaderExchangeHandler是一个比较核心的ChannelHandler类,在得到调用的结果之后它会调用NettyChannel.send(res)方法来发送结果。

dubboRpc远程调用如何熔断_客户端_13

3.2 rpc层分析

rpc层就是一个invoker链,重点是最后一个invoker类,就是我红框圈出来的部分 AbstractProxyInvoker

dubboRpc远程调用如何熔断_dubboRpc远程调用如何熔断_10

3.2.1 AbstractProxyInvoker

AbstractProxyInvoker持有了服务对象,并通过反射来调用服务对象的目标方法

dubboRpc远程调用如何熔断_java_15


对,这里是结尾,很仓促对不对hhhhhh