Liuqichun's Blog

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 前的包装逻辑

一条读写路径通常是:

  1. 内核数据进 Channel,Pipeline 从 head 开火:解码 → 业务 Handler
  2. 业务里 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());

常用方法:

每个 Handler 进链时会包一层 ChannelHandlerContext。Context 才是 Handler 在这条 Pipeline 上的「座位」,下一篇专门讲它。

从哪点火,决定从哪开始传

这点最容易和「事件总是从头走到尾」搞混:

业务 Handler 里如果调用的是 channel.writeAndFlush,消息会重新从尾巴走完整条出站链,可能再次经过自己后面的 Encoder,也可能绕回已经处理过的节点。只想交给「我前面的编码器」,用 ctx.writeAndFlush。

和 Channel、EventLoop 的关系

入站处理一半要中止,不要再 fireChannelRead;出站写完记得 flush,或者直接 writeAndFlush。入站读到的 ByteBuf 如果不会再往下传,要自己 release,否则会漏内存。

和上一篇、下一篇的衔接

系列(三)里的 ChannelHandler 是「节点上干什么」。本文的 Pipeline 是「节点怎么串起来、事件朝哪边走」。系列(五)的 ChannelHandlerContext 是「坐在某个座位上,怎么只通知邻居,而不是整条链重来一遍」。

(end)

<< Previous Post

|

Next Post >>

#Netty