Netty 之 Pipeline
{Back to Index}
Table of Contents
1 Pipeline 组成
Figure 1: Pipeline 内部结构(TailContext 和 HeadContext 既是 Context 也是 Handler)
Netty 引入 ChannelHandlerContext 来封装 ChannelHandler 的原因,在代码设计上还是遵循单一职责的原则, ChannelHandler 是用户接触最频繁的一个 netty 组件,netty 希望用户能够把全部注意力放在最核心的 IO 处理上,用户只需要关心自己对哪些异步事件感兴趣并考虑相应的处理逻辑即可,而并不需要关心异步事件在 pipeline 中如何传递,如何选择具有执行条件的 ChannelHandler 去执行或者跳过。这些切面性质的逻辑,netty 将它们作为上下文信息全部封装在 ChannelHandlerContext 中由netty框架本身负责处理。
1.1 ChannelHandlerContext
Pipeline 中的每个节点都是一个 ChannelHandlerContext ,在 ChannelHandlerContext 内部保存着 Handler 。
1.2 TailContext
Figure 2: TailContext 性质属于 Inbound 类型,但是具有处理 Outbound 数据的能力
1.3 HeadContext
Figure 3: HeadContext 性质属于 Outbound 类型,但是也能处理 Inbound 数据
1.3.1 处理 Outbound 数据
HeadContext 是处理 Outbound 数据的 最后一站 ,处理主要分为两部分:
- 将数据写入 buffer 队列
- 刷新 buffer 队列
1.3.1.1 写入 buffer 队列
主要有下列几个步骤:
- 将 ByteBuf 转化为 DirectByteBuf
- 将 ByteBuf 包装成 Entry 插入写队列
- 设置 writable 状态
1.3.1.2 刷新 buffer 队列
主要有下列几个步骤:
- 更新 Entry 指针和 channel 写状态
- 过滤出需要 flush 的 ByteBuf
- 使用 JDK 底层 API 将数据写出
2 类继承关系
3 传播路径
3.1 通过 ctx 触发的读事件
3.2 通过 channel 触发的读事件
3.3 通过 ctx 触发的写事件
3.4 通过 channel 触发的写事件
Figure 8: 起始点为 TailContext
3.5 异常的传播
Figure 9: 按照 pipeline 中的节点顺序依次向后传播
在 inbound 事件传播的过程中发生异常,才会回调 exceptionCaught 。
因为 inbound 事件一般都是由 netty 内核触发传播的,而 outbound 事件一般都是由用户选择触发的,比如用户在处理完业务逻辑触发的 write 事件或者 flush 事件。
而在用户触发 outbound 事件后,一般都会得到一个 ChannelPromise 。用户可以向 ChannelPromise 添加各种 listener 。当 outbound 事件在传播的过程中发生异常时,netty 会通知用户持有的这个 ChannelPromise ,但不会触发 exceptionCaught 的回调。
而 outbound 事件中只有 flush 事件的传播是个例外 ,当 flush 事件在 pipeline 传播的过程中发生异常时,会触发对应异常 ChannelHandler 的 exceptionCaught 事件回调。 因为 flush 方法的签名中不会给用户返回 ChannelPromise 。
4 传播逻辑
4.1 Inbound 事件的传播