Easy Netty 系列(四):ChannelPipeline
ChannelPipeline 是 ChannelHandler 链的容器,可以说是业务逻辑处理的大动脉,所有的 IO 事件都在这里流转。
ChannelPipeline 负责 ChannelHandler 的编排,其次是传递 Channel 的事件通知。

通过图说明 ChannelPipeline 的要点
1、每一个 Channel 创建时都会配一条自己的 ChannelPipeline,一对一绑定,Channel 关掉 Pipeline 也就废了。
2、Pipeline 是带 head、tail 哨兵节点的双向链表。入站事件从头走向尾,出站事件从尾走向头。
3、channel.write() / pipeline.write() 是出站,从 tail 往前找 OutboundHandler,不是从头部开始。
4、addLast(A, B, C) 之后:入站顺序是 A → B → C,出站顺序是 C → B → A。图里若从左到右编号 1~4,入站第一个是最左边,出站第一个是最右边。
入站和出站分别走哪边
| 方向 | 典型事件 | 起点 | 常见 Handler |
|---|---|---|---|
| 入站 Inbound | channelRead、channelActive、exceptionCaught |
head → tail | Decoder、SimpleChannelInboundHandler |
| 出站 Outbound | write、flush、bind、connect、close |
tail → head | Encoder、writeAndFlush 前的包装逻辑 |
一条读写路径通常是:
- 内核数据进 Channel,Pipeline 从 head 开火:解码 → 业务 Handler
- 业务里
ctx.writeAndFlush(msg),从当前节点的前一个出站 Handler往回走:编码 → 真正写 socket
所以编解码器常常成对出现:入站 Decoder 靠前,出站 Encoder 靠后(addLast 时 Encoder 可以和 Decoder 一起加,方向由接口决定,不会走错链)。
怎么往 Pipeline 里加 Handler
Server 端一般在 ChannelInitializer.initChannel 里组链:
1ch.pipeline().addLast("decoder", new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4));
2ch.pipeline().addLast("encoder", new LengthFieldPrepender(4));
3ch.pipeline().addLast("handler", new EchoServerHandler());
常用方法:
addLast/addFirst:按添加顺序排addBefore/addAfter:插到某个名字旁边remove/replace:运行期换协议时很有用,比如先 HTTP 握手再升级成 WebSocket
每个 Handler 进链时会包一层 ChannelHandlerContext。Context 才是 Handler 在这条 Pipeline 上的「座位」,下一篇专门讲它。
从哪点火,决定从哪开始传
这点最容易和「事件总是从头走到尾」搞混:
channel.pipeline().fireChannelRead(msg):从 head 开始入站channel.write(msg)/pipeline.write(msg):从 tail 开始出站ctx.fireChannelRead(msg):从当前 Context 的下一个入站节点继续ctx.write(msg):从当前 Context 的前一个出站节点继续
业务 Handler 里如果调用的是 channel.writeAndFlush,消息会重新从尾巴走完整条出站链,可能再次经过自己后面的 Encoder,也可能绕回已经处理过的节点。只想交给「我前面的编码器」,用 ctx.writeAndFlush。
和 Channel、EventLoop 的关系
- Channel:连接本身,读写下层 socket
- Pipeline:这条连接上的处理链
- EventLoop:跑这条 Channel 上所有回调的线程。同一 Channel 的 Pipeline 事件是串行的,Handler 里一般不用自己加锁
- Handler:具体逻辑。可以
@Sharable挂到多条 Pipeline,这时不能在 Handler 里放连接级状态
入站处理一半要中止,不要再 fireChannelRead;出站写完记得 flush,或者直接 writeAndFlush。入站读到的 ByteBuf 如果不会再往下传,要自己 release,否则会漏内存。
和上一篇、下一篇的衔接
系列(三)里的 ChannelHandler 是「节点上干什么」。本文的 Pipeline 是「节点怎么串起来、事件朝哪边走」。系列(五)的 ChannelHandlerContext 是「坐在某个座位上,怎么只通知邻居,而不是整条链重来一遍」。
(end)