ChannelPipeline 接口

每一个新创建的Channel都将会被分配一个新的ChannelPipeline。这项关联是永久性的;Channel既不能附加另外一个ChannelPipeline,也不能分离当前的。

根据事件的起源,事件将会被ChannelInboundHandler或者ChannelOutboundHandler处理。随后,会调用ChannelHandlerContext实现,它将被转发给同一超类型的下一个ChannelHandler。

在ChannelPipeline传播事件时,它会测试ChannelPipeline中的下一个ChannelHandler的类型是否和事件的运动方向相匹配。

如果不匹配,ChannelPipeline将跳过该ChannelHandler并前进到下一个,直到它找到和该事件所期望的方向相匹配的为止。

(ChannelHandler可以同时实现ChannelInboundHandler和ChannelOutboundHandler接口)

ChannelHandlerContext

ChannelHandlerContext使得ChannelHandler能够和它的ChannelPipeline以及其他的ChannelHandler 交互。

ChannelHandler 可以通知其所属的 ChannelPipeline 中的下一个 ChannelHandler,甚至可以动态修改它所属的ChannelPipeline。

ChannelHandlerContext 具有丰富的用于处理事件和执行I/O 操作的API。

ChannelPipeline 相对论

你可能会说,从事件途经ChannelPipeline 的角度来看,ChannelPipeline 的头部和尾端取决于该事件是入站的还是出站的。

然而Netty 总是将ChannelPipeline 的入站口(图6-3 中的左侧)作为头部,而将出站口(该图的右侧)作为尾端。

当你完成了通过调用ChannelPipeline.add*()方法将入站处理器(ChannelInboundHandler)和出站处理器( ChannelOutboundHandler ) 混合添加到ChannelPipeline 之后, 每一个ChannelHandler 从头部到尾端的顺序位置正如同我们方才所定义它们的一样。

因此,如果你将图6-3 中的处理器(ChannelHandler)从左到右进行编号,那么第一个被入站事件看到的ChannelHandler 将是1,而第一个被出站事件看到的ChannelHandler 将是5。

修改ChannelPipeline

ChannelHandler 可以通过添加、删除或者替换其他的 ChannelHandler 来实时地修改 ChannelPipeline 的布局。

(它也可以将它自己从ChannelPipeline 中移除。)这是ChannelHandler 最重要的能力之一,所以我们将仔细地来看看它是如何做到的。

表6-6 列出了相关的方法。

  [plaintext]
1
2
3
4
5
6
7
名称 描述 addFirst(),addBefore(),addAfter(),addLast() 将一个ChannelHandler添加到ChannelPipeline remove() 将一个ChannelHandler从ChannelPipeline中移除 replace() 将ChannelPipeline中的一个ChannelHandler替换为另一个ChannelHandler get() 通过类型或者名称返回ChannelHandler context() 返回和ChannelHandler绑定的ChannelHandlerContext names() 返回ChannelPipeline中所有的ChannelHandle的名称

触发事件

ChannelPipeline 的API 公开了用于调用入站和出站操作的附加方法。

入站操作

表 6-8 列出了入站操作,用于通知 ChannelInboundHandler 在 ChannelPipeline 中所发生的事件。

  [plaintext]
1
2
3
4
5
6
7
8
9
10
名称 描述 fireChannelRegistered 调用ChannelPipeline中下一个ChannelInboundHandler的channelRegistered(ChannelHandlerContext)方法 fireChannelUnregistered 调用ChannelPipeline中下一个ChannelInboundHandler的channelUnregistered(ChannelHandlerContext)方法 fireChannelActive 调用ChannelPipeline中下一个ChannelInboundHandler的channelActive(ChannelHandlerContext) fireChannelInActive 调用ChannelPipeline中下一个ChannelInboundHandler的channelInactive(ChannelHandlerContext)方法 fireExceptionCaught 调用ChannelPipeline中下一个ChannelInboundHandler的exceptionCaught(ChannelHandlertext, Throwable)方法 fireUserEventTriggerd 调用ChannelPipeline中下一个ChannelInboundHandler的userEventTriggered(ChannelHandlertext, Object)方法 fireChannelRead 调用ChannelPipeline中下一个ChannelInboundHandler的channelRead(ChannelHandlertext, Object msg)方法 fireChannelReadComplete 调用ChannelPipeline中下一个ChannelInboundHandler的channelReadComplete(ChannelHandlertext)方法 fireChannelWritabilityChanged 调用ChannelPipeline中下一个ChannelInboundHandler的channelWritabilityChanged(ChannelHandlertext)方法

出站操作

在出站这边,处理事件将会导致底层的套接字上发生一系列的动作。

表6-9 列出了ChannelPipeline API 的出站操作。

  [plaintext]
1
2
3
4
5
6
7
8
9
10
名称 描述 bind 将Channel绑定到一个本地地址,将调用ChannelPipeline中的下一个ChannelOutboundHandler的bind(ChannelHandlerContext,Socket,ChannelPromise)方法 connect 将Channel连接到一个远程地址,这将调用ChannelPipeline中的下一个ChannelOutboundHandler的connect(ChannelHandlerContext,Socket,ChannelPromise)方法 disconnect 将Channel断开连接。这将调用ChannelPipeline中的下一个ChannelOutboundHandler的disconnect(ChannelHandlerContext,Socket,ChannelPromise)方法 close 将Channel关闭。这将调用ChannelPipeline中的下一个ChannelOutboundHandler的close(ChannelHandlerContext,ChannelPromise)方法 deregister 将Channel从它先前分配的EventExecutor(即EventLoop)中注销,这将调用ChannelPipeline中的下一个ChannelOutboundHandler的deregister(ChannelHandlerContext,ChannelPromise)方法 flush 冲刷Channel所有挂起的写入。这将调用ChannelPipeline中的下一个ChannelOutboundHandler的flush(ChannelHandlerContext)方法 write 将消息写入Channel。这将调用ChannelPipeline中的下一个ChannelOutboundHandler的write(ChannelContext,Object msg,ChannelPromise)方法。这并不会将消息写入底层的Socket,而只会将它放入到队列中。要将它写入到Socket,需要调用flush或者writeAndFlush方法 writeAndFlush 先调用write再调用flush的便利方法 read 请求从Channel中读取更多的数据。这将调用ChannelPipeline中的下一个ChannelOutboundHandler的read(ChannelHandlerContext)方法

总结

ChannelPipeline保存了与Channel相关联的ChannelHandler

ChannelPipeline可以根据需要,通过添加或者删除ChannelHandler来动态修改

ChannelPipeline有着丰富的API调用,以响应入站和出站事件

个人收获

  1. 为每一张图,没一个代码片段,每一个表格添加标号是一种非常不错的方式。特别是在写书籍的时候。

参考资料

《Netty in Action》 P99

  • free

Netty ByteBuf 释放注意事项