从上一篇文章Dubbo的编解码方式可以看出,在远程调用这一块,Dubbo主要分为2层:传输层和交换层。其中传输层(Transport)只负责对二进制数据的收发,交换层(Exchange)负责对Dubbo协议的编解码,然后调用Transport层的接口收发数据,其大致流程如下所示:
在介绍Dubbo的Channel和ChannelHandler之前,我们先看一下Netty的框架设计。
Netty中的Channel是对网络Socket的封装,通过Channel可以和网络对端进行数据的收发。在数据收发的过程中,会经过入站和出站的ChannelHandler处理。通常来说在连接建立/数据发送/数据接受等阶段,在ChannelHandler会产生对应的事件回调。这样ChannelHandler可以根据事件的类型,执行具体的处理逻辑。
而Dubbo的网络层也是参考了Netty的设计,重新定义了Channel和ChannelHandler,下面我们依次看一下其类之间的关系及具体的代码实现。
Channel
从上面的继承关系可以看出,Channel继承了Endpoint接口,Endpoint是对一个网络节点的抽象,具有着数据收发,获取Dubbo的URL等功能。而Channel在Endpoint的基础上,又增加了属性存储的方法。
而对于AbstractChannel,其实现了Channel接口之外,又继承了AbstractPeer,AbstractChannel类本身并没有逻辑性的代码。下面我们看下AbstractPeer的实现:
AbstractPeer是后面即将谈到的Server/Client的抽象父类,从上面的属性可以看出其有着ChannelHandler和URL。下面我们看下NettyChannel的实现:
首先NettyChannel有个static的变量,里面保存着Netty的Channel到自身对象的映射Map,然后就是组合了Netty的Channel和当前Dubbo的Channel属性的attributes。也即是说Dubbo的Channel通过组合通信框架(如Netty)的Channel,来实现自身通信的功能。
ChannelHandler
对于ChannelHandler来说,从名字上就可以看出是对Channel上产生的一些列事件,所产生的事件回调触发。这个我们可以类比Netty的ChannelHandler,也可以看下Dubbo的ChannelHandler定义:
从上面接口声明可以看出,当有连接建立或断开,数据接收或发送等网络事件触发时,会回调ChannelHandler的对应的方法,让调用方执行相应的业务逻辑。
对于ChannelHandler而言下面主要分2个方面讲述:基于ChannelHandler所形成的Server和Client和ChannelHandler触发的执行模式。
Server和Client
从上图可以看出,AbstractPeer具有Endpoint(网络节点)和ChannelHandler(网络事件回调)的能力。在AbstractPeer的基础上,AbstractEndpoint又增加了编码器和重置超时时间的能力。其核心代码如下所示:
对于Server端,我们看下AbstractServer所实现的一个接口Server。
除了网络节点和网络事件处理的能力之外,在Server接口上又增加了,获取连接到服务端的所有Channel,和根据IP获取对应的Channel等。对于Server这条线,我们下面看下AbstractServer的实现:
从AbstractServer的核心代码可以看出,抽象类提供了创建的模板方法,其中包含了回调子类的doOpen(),去完成一个真正的端口监听。从属性上看,上面有服务端限制连接的最大数accepts和绑定的IP等信息。再往下走,就到了具体的实现类了。我们看一下Netty4的实现(dubbo 2.6.0):
从上面的Netty4的实现可以看出,在构造器中,直接调用了AbstractServer的构造器。不过对ChannelHandler做了包装处理,形成了异步处理的效果。然后AbstractServer会调用子类的doOpen()方法,进入具体的IP绑定和服务启动。这里对于Netty4本身的一些API就不做过多的解释了,这里我们来看下在pipeline中设置的编解码及处理请求响应的Handler。
对于编解码的Handler,这里面使用了NettyCodecAdapter去封装了编码和解码器,这两个编解码器分别是作为内部类实现的,这里我们先看下NettyCodecAdapter的核心属性:
可以看出,除了编码和解码器,还有具体的编解码实现Codec2,这里面通常是DubboCodec。其他的属性都是辅助编码实现的,下面我们分别看下InternalEncode和InternalDecode的实现
编码器还是相对比较简单的,对于Netty4的适配,直接继承了MessageToByteEncoder。在重写encode的方法里,直接使用了Codec2(通常为DubboCodec,新版本使用的DubboCountCodec也是对DubboCodec的简单封装)的编码。对于InternalDecoder而言,实现如下:
可以看出,当解码时如果此时接收到的字节数不完整,此时会解码器会返回 NEED_MORE_INPUT,此时会继续接受数据,直到接收到完整的报文,最终完成解码过程。
ChannelHandler
ChannelHandler可以立即为在消息收发之后的一个回调事件。在通讯框架层(如Netty),ChannelHandler主要可以分为2类:编解码器和业务处理器。对于Dubbo而言,ChannelHandler指的就是业务处理器。而对于编解码器,前面已经说过了,这里不再赘述。
下面我们解析一下ChannelHandler的类图结构,上面的结构是ChannelHandler执行模式的类图。也就是说,它支持了ChannelHandler里面的方法,不同的异步执行模式。
首先对于ChannelHandler有个WrappedChannelHandler实现,里面的逻辑就是直接调用原ChannelHandler的对应connected、sent等方法。对于异步执行,Dubbo又分离出了4个类型出来:Default(所有方法异步)、Execution(除了sent方法是同步,其他的都是异步)、ConnectionOrdered(connect和disconnect使用单线程的线程池,received和caught都是异步,sent同步)、MessageOnly(只有received是异步,其余都是同步)。WrapperChannelHandler使用了装饰模式,将原先同步执行的ChannelHandler,装饰成了不同异步模式执行的ChannelHandler。
而对于下面蓝色的框里面的类,看起来和上面紫色的类名非常类似。这里面主要是方便 Dubbo SPI 的调用,其代码中也没有具体的业务逻辑,只是直接new了对应的紫色类的对象,最终通过ChannelHandlers工具类,暴露了转异步的能力。最终的结果就是在原先的ChannelHandler套了一层。
除此之外,右上方还有2个橙色的类实现。其中ChannelHandlerDispatcher 就是一个批量操作,当传入多个ChannelHandler时,循环调用Handler数组中的对应方法。ChannelHandlerAdapter则是给Exchange层的ExchangeHandlerAdapter新增的适配,但其实现也为空,最终的使用方是 DubboProtocol的requestHander 内部属性实现。
接下篇《 Dubbo传输层及交换层(二)》
参考:Dubbo 2.6.0 源代码、Dubbo 2.0.7 源代码