导读
原创文章,转载请注明出处。
本文源码地址:netty-source-code-analysis
本文所使用的netty版本4.1.6.Final:带注释的netty源码
Pipeline
这个词翻译过来就是“流水线”的意思,读到这里有了解过设计模式的同学应该已经想到了,这里用到的是“责任链模式”。本文我们将以DefaultChannelPipeline
为例看一下Pipeline
的构造以及其中重要的数据结构。
1 和Pipeline相关的其他组件
1.1 ChannnelHandler
这是ChannelHandler
中的注释,翻译过来就是“处理IO事件或者拦截IO操作,并且将其向ChannelPipeline
中的下一个handler
传递”,说白了就是在责任链中注册的一系列回调方法。
Handles an I/O event or intercepts an I/O operation, and forwards it to its next handler in
its ChannelPipeline
这里的I/O event
就是很多书中提到的“入站事件”,而I/O operation
就是很多书中提到的“出站事件”,前面我说过,这里我并不准备这么叫,按我的理解我习惯把这两者称之为“事件”和“命令”。很显然这里event
和operation
的含义是不一样的,event
更多地多表示事件发生了,我们被动地收到,而operation
则表示我们主动地发起一个动作或者命令。
1.2 ChannelHandlerContext
每一个ChannelHandler
在被添加进ChannelPipeline
时会被包装进一个ChannelHandlerContext
。有两个特殊的ChannelHandlerContext
除外,分别是HeadContext
和TailContext
,HeadContext
继承了ChannelInBoundHandler
和ChannelOutBoundHandler
,而TailContext
继承了ChannelInBoundHandler
。
每个ChannelHandlerContext
中有两个指针next
和prev
,这是用来将多个ChannelHandlerContext
构成双向链表的。
2 Pipeline的构造方法
我们以DefaultChannelPipeline
为例,从它的构造方法开始。这里首先将Channel
保存到Pipeline
的属性中,又初始化了两个属性succeedFuture
和voidPromise
。这是两个特殊的可以共享的Promise
,这两个Promise
不是重点,不理解也没关系。
接下来的tail
和head
是两个特殊的ChannelHandlerContext
,这两个是Pipeline
中的重要组件。
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
Pipeline在执行完构造方法以后的结构如下图所示,head
和tail
构成了最简单的双向链表。
图中蓝色填充的就是ChannelHandlerContext
,目前只有HeadContext
和TailContext
,ChannelHandlerContext
中的较窄的矩形表示ChannelHandler
,由于HeadContext
和TailContext
并没有包含ChannelHandler
,而是继承ChannelHandler
,所以这里我们用虚线表示。上下贯通的ChannelHandler
表示既是ChannelInBoundHandler
又是ChannelOutBoundHandler
,只有上半部分的表示是ChannelInBoundHandler
,只有下半部分的表示是ChannelOutBoundHandler
。
3 添加ChannelHandler
在ChannelPipeline
中有很多以add
开头的方法,这些方法就是向ChannelPipeline
中添加ChannelHandler
的方法。
addAfter
:向某个ChannelHandler
后边添加addBefore
:向某个ChannelHandler
前面添加addFirst
:添加到头部,不能在head
的前面,而是紧挨着head
,在head
的后面addLast
:添加到尾部,不能在tail
的后面,而是紧挨着tail
,在tail
的前面
我们以最常用的的addLast
方法为例来分析一下Pipeline
中添加ChannelHandler
的操作。
这里所贴出的addLast
方法其实我们已经在“服务端启动流程”这篇文章中打过照面了。方法参数中的EventExecutorGroup
意味着我们可以为这个ChannelHandler
单独设置Excutor
而不使用Channel
所绑定的EventLoop
,一般情况下我们不这么做,所以group
参数为null
。
这里先把ChannelHandler
包装成ChannelHandlerContext
,再添加到尾部,随后调用ChannelHandler
的HandlerAdded
方法。
在调用HandlerAdded
方法时有一点问题,添加ChannelHandler
的操作不需要在EventLoop
线程中进行,而HandlerAdded
方法则必须在EventLoop
线程中进行。也就是说存在添加Handler
时还未绑定EventLoop
的情况,此时则调用newCtx.setAddPending()
将当前HandlerContext
设置为ADD_PENDING
状态,并且调用callHandlerCallbackLater(newCtx, true)
将一个异步任务添加到一个单向队链表中,即pendingHandlerCallbackHead
这个链表。
如果当前已经绑定了EventLoop
,则看当前调用线程是否为EventLoop
线程,如果不是则向EventLoop
提交一个异步任务调用callHandlerAdded0
方法,否则直接调用callHandlerAdded0
方法。
下面咱们依次分析一下newContext
,callHandlerCallbackLater
和callHandlerAdd0
方法。
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
//先把`handler`包装成`HandlerContext`
newCtx = newContext(group, filterName(name, handler), handler);
//添加到尾部
addLast0(newCtx);
//如果还未绑定`EventLoop`则稍后再发起对`HandlerAdded`方法的调用。
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this