鉴于Iteye上的人气越来越少,打算把上面的文章搬过来了。今天先搬Netty相关的。
下面是用excel画的一个简单的结构图
- Channel是对最终I/O处理的封装
- EventExecutor 封装了负责处理I/O 事件的线程
- ChannelHandler 处理相关I/O Event的扩展接口,分为ChannelInboundHandler和[*]ChannelOutboundHandler,分别处理不同流向的事件
- ChannelHandlerContext 对ChannelHandler相关信息的包装
- ChannelPipeline 组装多个ChannelHandlerContext的管道,I/O事件在这个管道中流动
姑且给角色A取名命令者,角色B取名执行者。所以这里会有两个过程。一个是命令者通知执行者。一个是执行者通知命令者。在网络通信过程中这两个角色容易理解,命令者就是执行应用逻辑的线程,执行者是底层执行具体I/O的线程。
具体体现到类上的,EventExecutor就是I/O操作的执行者了,命令者通知执行者的过程描述是ChannelOutboundInvoker,而执行者通知命令者的过程描述就是ChannelInboundInvoker,这里的Inbound和Outbound是面向命令者来说的,而实际应用中的命令者就是应用逻辑了,netty本身作为一个框架肯定就是服务于应用的。对比ChannelOutboundInvoker和ChannelInboundInvoker中的方法名称,发现Inbound的中的都是firexxx,fire对通知是再形象不过的描述了。而Outbound中的都是xxx(ChannelPromise promise),命令口气十足啊,同时还给了执行者一个ChannelPromise,相当于告诉了执行者执行完后,把执行结果放到指定的地方,命令者在接收到执行者执行完成的通知后,就去前面指定的ChannelPromise中去取结果。
ChannelPipeline描述的是命令者与执行者交互的过程,所以它既继承了ChannelInboundInvoker也继承了ChannelOutboundInvoker。
ChannelHandlerContext是针对ChannelHandler的包装,处于ChannelPipeline中,所以它也同时实现了两个。
Channel是负责与外部进行I/O的执行者的抽象,所以它继承了ChannelOutboundInvoker。
首先分析下Channel接口,接口文档中可以了解到:这个接口主要是封装了一些网络的I/O操作read,write,connect,bind。另外绑定了一下Channel相关的信息。ChannelConfig指定Channel的相关配置信息,ChannelPipeline负责命令者和执行者Channel之间的I/O事件传递。
有意思的是内部的UnSafe接口了,接口文档描述的是之所以取名UnSafe是因为不能从外部线程调用UnSafe接口中的方法,只能在Channel当前相关的I/O线程中调用。有一些接口除外,这个具体看下文档。UnSafe中才是真正的I/O操作实现,里面包含了register,bind,connect,disconnect,close,read,write,flush等操作,具体实现可以看子类了。
这里先描述下具体I/O操作调用的流程,
应用->Channel的I/O操作->调用Pipeline相应的I/O操作->调用ChannelHandlerContext的相应I/O操作->调用ChannelHandler的相应操作->Channel.UnSafe中相关的I/O操作。
应用为什么不直接调用Channel.UnSafe接口中的I/O操作呢,而要绕一个大圈呢?因为它是框架,要支持扩展。
执行者完成操作后,是如何通知命令者的呢?一般流程是这样的:
Channel.UnSafe中执行相关的I/O操作,根据操作结果->调用ChannelPipeline中相应发fireXXXX()接口->调用ChannelHandlerContext中相应的fireXXXX()接口->调用ChannelHandler中相应方法->调用应用中的相关逻辑
后面将按照上图中的各个元素逐一分析他们的源码。