DefaultChannelPipeline 工作
构造函数
DefaultChannelPipeline的构造器需要传入一个Channel,而这个Channel其实就是我们实例化的NioSocketChannel对象,DefaultChannelPipeline会将这个NioSocketChannel对象保存在Channel属性中。DefaultChannelPipeline中还有两个特殊的属性,即Head和Tail,这两个属性是双向链表的头和尾。其实在DefaultChannelPipeline中维护了一个以AbstractChannelHandlerContext为节点元素的双向链表,这个链表是Netty实现Pipeline机制的关键
inbound和outbound
inbound事件
在netty的pipeline中包含两种类型的事件,分别为inbound和outbound,inbound为上行事件,outbound为下行事件。inbound事件为被动触发,在某些情况发生时自动触发;outbound为主动触发,在需要主动执行某些操作时触发。
inbound事件一般通过pipeline的fire**方法触发,包含如下
1. fireChannelRegistered channel注册事件,为inbound事件。
2. fireChannelUnregistered channel解除注册事件,为inbound事件。
3. fireChannelActive channel活跃事件,为inbound事件。
4. fireChannelInactive channel非活跃事件,为inbound事件。
5. fireExceptionCaught 异常事件,这个事件会在所有handler里传播。
6. fireUserEventTriggered用户事件,为inbound事件。
7. fireChannelRead channel读事件,为inbound事件
8. fireChannelReadCompletechannel读完成事件,为inbound事件。
9. fireChannelWritabilityChanged channel写状态变化事件,为inbound事件。
在pipeline中通过fire触发的事件都会调用AbstractChannelHandlerContext对应的invoke方法,而且都会执行head的invoke方法,该方法会调用该context的handler的对应处理方法,然后调用该context的fire方法向后面的inbound context传播。
fireChannelActive
当建立连接之后,Channel处于Active,向PipeLine发送ChannelActive 事件,为inbound事件,从HeadText开始
HeadText的下一个inbound事件是我们定义的DefautChannelHandlerContext,它的Handler对应的是ServerBootstrapAcceptor(ChannelInboundHandler的实现类)
ServerBootstrapAcceptor作为Handler开始工作
fireChannelRead 感兴趣的事件到来 —— 触发读操作(可读事件)
注意这里和下面的read事件是两个完全不同的概念和功能。
具体过程 Netty 处理请求(一)感兴趣的事件到来 读操作
inbound事件,从前往后处理
当channel注册到selector时触发 fireChannelRegistered
当channel注册到selector时,触发pipeline的fireChannelRegistered,从head开始遍历,找到实现了ChannelInboundHandler接口的handler,并执行其fireChannelRegistered方法。
invokeChannelRegistered(final AbstractChannelHandlerContext next)
从head开始执行invokeChannelRegistered
invokeChannelRegistered()
invokeHandler
节点状态
关键点触发 Channelinitialize#channelRegistered
HeadContext#channelRegistered
可以看出inbound的事件传播机制,从HeadText开始从后寻找InboundContext
outbound事件
outbound事件包含如下:
1. bind 端口绑定事件。
2.connect 连接事件。
3.disconnect 断开连接事件。
4.close 关闭事件。
5.deregister 解除注册事件
6.flush 刷新数据到网络事件。
7.read 读事件,在创建好NioSocketChannel后会触发该事件,用于注册该channel的OP_READ感兴趣事件到event loop的selector。
8.write 写事件。
9.writeAndFlush 写出数据事件。
DefaultChannelPipeline#bind 端口绑定事件
AbstractChannelHandlerContext#bind
最终跳转到HeadContext的bind方法
read 读事件
在完成channelActife事件处理之后,会执行HeadContext.readIfAutoRead
HeadContext.readIfAutoRead
read是个outbound事件,所以从tail开始向前处理
其实read的操作还是通过HeadText完成
AbstractUnsafe#beginRead
当然,最终还是交由Channel来实现
在这之前已经建立了连接,这里属于建立连接之后有感兴趣的事件传来时要做的工作,
工作场景
addLast()系列
ServerBootstrap执行init方法时,会向pipeline添加一个ChannelHandler
核心方法
总结一下:
(1)保证该ChannelHandler之前没有加过或者加过但是可以共享checkMultiplicity
(2)根据该Handler创建一个HandlerContext, newCtx = this.newContext(group, this.filterName(name, handler), handler); 这里的名字也要唯一,所以也需要提前判断一下
(3)
checkMultiplicity 判断ChannelHandler是否已存在(保证唯一性)
newContext——每个Handler都会单独创建一个对应的HandlerContext放入PipeLine
说白了,PipeLine是用来保存信息的,最终只会通过信息跳转到你的处理过程,这样的好处不言而喻,PipeLine就像我们熟悉的Eureka一样,你可以把它理解为注册中心,而ChannelHandler就像是每个微服务,PipeLine保存微服务的信息,而HandlerContext正是对这种信息的封装。
这里会跳到DefaultChannelHandlerContext的创建过程。
addLast0 创建了HandlerContext之后就需要插入到PipeLine中
清晰易懂,插入到PipeLine的双向链表之中。
invokeHandlerAddedIfNeeded
这一步的意义就在于
PendingHandlerAddedTask#execute()
callHandlerAdded0()
调用ChannelHandler#handlerAdded(ctx)方法
两个重要节点
HandContext
可以看到是通过Unsafe来实现的,毕竟已经到底层实现了
bind方法
this.unsafe.bind()
首先判断一下条件是否就绪
Channel已经注册到Selector并且当前线程就是工作线程。
AbstractChannel.this.doBind(localAddress); 执行连接
TailContext
PendingHandlerCallback
pending具有等待的意思,也就是PendingHandlerCallback封装了正要被回调的ChannelHandler
PendingHandlerAddedTask
继承PendingHandlerCallback