1. 概述
在 《精尽 Netty 源码分析 —— Netty 简介(二)之核心组件》 中,对 EventLoopGroup 和 EventLoop 做了定义,我们再来回顾下:
ChannelPipeline 为 ChannelHandler 的链,提供了一个容器并定义了用于沿着链传播入站和出站事件流的 API 。一个数据或者事件可能会被多个 Handler 处理,在这个过程中,数据或者事件经流 ChannelPipeline ,由 ChannelHandler 处理。在这个处理过程中,一个 ChannelHandler 接收数据后处理完成后交给下一个 ChannelHandler,或者什么都不做直接交给下一个 ChannelHandler。
因为 ChannelPipeline 涉及的代码量较大,所以笔者会分成好几篇文章分别分享。而本文,我们来分享 ChannelPipeline 的初始化。也因此,本文更多是体现 ChannelPipeline 的整体性,所以不会过多介绍每个类的具体的每个方法的实现。
2. ChannelPipeline
io.netty.channel.ChannelPipeline
,继承 ChannelInboundInvoker、ChannelOutboundInvoker、Iterable 接口,Channel Pipeline 接口。代码如下:
public interface ChannelPipeline |
虽然接口的方法比较多,笔者做了归类如下:
- ChannelHandler 的增删改查的相关方法。
- Channel 的相关方法,目前只有一个。
- 继承自 ChannelInboundInvoker 的相关方法。
- 继承自 ChannelOutboundInvoker 的相关方法。
有可能会疑惑为什么继承 Iterable 接口?因为 ChannelPipeline 是 ChannelHandler 的链。
ChannelPipeline 的类图如下:
2.1 ChannelInboundInvoker
io.netty.channel.ChannelInboundInvoker
,Channel Inbound Invoker( 调用者 ) 接口。代码如下:
ChannelPipeline fireChannelRegistered(); |
- 通知 Channel 事件的接口方法。
2.2 ChannelOutboundInvoker
io.netty.channel.ChannelOutboundInvoker
,Channel Outbound Invoker( 调用者 ) 接口。代码如下:
// ========== Channel 操作相关 ========== |
- 发起 Channel 操作的接口方法。
- 创建 Promise 对象的接口方法。
2.3 Outbound v.s Inbound 事件
在 《Netty 源码分析之 二 贯穿Netty 的大动脉 ── ChannelPipeline (二)》 中,笔者看到一个比较不错的总结:
老艿艿:因为要加一些注释,所以暂时不使用引用。
对于 Outbound 事件:
- Outbound 事件是【请求】事件(由 Connect 发起一个请求, 并最终由 Unsafe 处理这个请求)
- Outbound 事件的发起者是 Channel
- Outbound 事件的处理者是 Unsafe
Outbound 事件在 Pipeline 中的传输方向是
tail
->head
旁白:Outbound 翻译为“出站”,所以从
tail
( 尾 )到head
( 头 )也合理。至于什么是
head
和tail
,等看了具体的 ChannelPipeline 实现类 DefaultChannelPipeline 再说。在 ChannelHandler 中处理事件时, 如果这个 Handler 不是最后一个 Handler, 则需要调用
ctx.xxx
(例如ctx.connect
) 将此事件继续传播下去. 如果不这样做, 那么此事件的传播会提前终止.- Outbound 事件流:
Context.OUT_EVT
->Connect.findContextOutbound
->nextContext.invokeOUT_EVT
->nextHandler.OUT_EVT
->nextContext.OUT_EVT
对于 Inbound 事件:
- Inbound 事件是【通知】事件, 当某件事情已经就绪后, 通知上层.
- Inbound 事件发起者是 Unsafe
- Inbound 事件的处理者是 TailContext, 如果用户没有实现自定义的处理方法, 那么Inbound 事件默认的处理者是 TailContext, 并且其处理方法是空实现.
Inbound 事件在 Pipeline 中传输方向是
head
( 头 ) ->tail
( 尾 )旁白:Inbound 翻译为“入站”,所以从
head
( 头 )到tail
( 尾 )也合理。在 ChannelHandler 中处理事件时, 如果这个 Handler 不是最后一个 Handler, 则需要调用
ctx.fireIN_EVT
(例如ctx.fireChannelActive
) 将此事件继续传播下去. 如果不这样做, 那么此事件的传播会提前终止.- Inbound 事件流:
Context.fireIN_EVT
->Connect.findContextInbound
->nextContext.invokeIN_EVT
->nextHandler.IN_EVT
->nextContext.fireIN_EVT
Outbound 和 Inbound 事件十分的镜像, 并且 Context 与 Handler 直接的调用关系是否容易混淆, 因此读者在阅读这里的源码时, 需要特别的注意。
3. DefaultChannelPipeline
io.netty.channel.DefaultChannelPipeline
,实现 ChannelPipeline 接口,默认 ChannelPipeline 实现类。😈 实际上,也只有这个实现类。
3.1 静态属性
/** |
HEAD_NAME
和TAIL_NAME
静态属性,通过调用#generateName0(Class<?> handlerType)
方法,生成对应的名字。代码如下:private static String generateName0(Class<?> handlerType) {
return StringUtil.simpleClassName(handlerType) + "#0";
}- 即
HEAD_NAME = "HeadContext#0"
,TAIL_NAME= "TailContext#0"
。
- 即
nameCaches
静态属性,名字(AbstractChannelHandlerContext.name
)缓存 ,基于 ThreadLocal ,用于生成在线程中唯一的名字。详细解析,见 《精尽 Netty 源码解析 —— Pipeline(二)之添加 ChannelHandler》 。ESTIMATOR
静态属性,estimatorHandle
属性的原子更新器。
3.2 构造方法
/** |
head
属性,Head 节点,在构造方法的<1>
处初始化。详细解析,见 「4.2 HeadContext」 。tail
节点,Tail 节点,在构造方法的<2>
处初始化。详细解析,见 「4.3 TailContext」 。在构造方法的
<3>
处,head
节点向下指向tail
节点,tail
节点向上指向head
节点,从而形成相互的指向。即如下图所示:- pipeline 中的节点的数据结构是 ChannelHandlerContext 类。每个 ChannelHandlerContext 包含一个 ChannelHandler、它的上下节点( 从而形成 ChannelHandler 链 )、以及其他上下文。详细解析,见 「4. ChannelHandlerContext」 。
默认情况下,pipeline 有
head
和tail
节点,形成默认的 ChannelHandler 链。而我们可以在它们之间,加入自定义的 ChannelHandler 节点。如下图所示:
childExecutors
属性,子执行器集合。默认情况下,ChannelHandler 使用 Channel 所在的 EventLoop 作为执行器。- 但是如果有需要,也可以自定义执行器。详细解析,见 《精尽 Netty 源码解析 —— Pipeline(二)之添加 ChannelHandler》 。
- 实际情况下,基本不会用到。和基友【闪电侠】沟通过。
pendingHandlerCallbackHead
属性,准备添加 ChannelHandler 的回调。详细解析,见 《精尽 Netty 源码解析 —— Pipeline(二)之添加 ChannelHandler》 。registered
属性,Channel 是否已注册。详细解析,见 《精尽 Netty 源码解析 —— Pipeline(二)之添加 ChannelHandler》 。firstRegistration
属性,是否首次注册。详细解析,见 《精尽 Netty 源码解析 —— Pipeline(二)之添加 ChannelHandler》 。
3.3 其他方法
DefaultChannelPipeline 中的其他方法,详细解析,见后续的文章。
4. ChannelHandlerContext
io.netty.channel.ChannelHandlerContext
,继承 ChannelInboundInvoker、ChannelOutboundInvoker、AttributeMap 接口,ChannelHandler Context( 上下文 )接口,作为 ChannelPipeline 中的节点。代码如下:
// ========== Context 相关 ========== |
虽然接口的方法比较多,笔者做了归类如下:
- Context 相关的接口方法。
- 继承自 ChannelInboundInvoker 的相关方法,和 ChannelPipeline 一样。
- 继承自 ChannelOutboundInvoker 的相关方法,和 ChannelPipeline 一样。
- 继承自 AttributeMap 的相关方法,实际上已经废弃(
@Deprecated
)了,不再从 ChannelHandlerContext 中获取,而是从 Channel 中获取。
ChannelHandlerContext 的类图如下:
- 😈 类图中的 AttributeMap 和 DefaultAttributeMap 可以无视。
4.1 AbstractChannelHandlerContext
io.netty.channel.AbstractChannelHandlerContext
,实现 ChannelHandlerContext、ResourceLeakHint 接口,继承 DefaultAttributeMap 类,ChannelHandlerContext 抽象基类。
4.1.1 静态属性
/** |
handlerState
属性( 非静态属性,放这里主要是为了统一讲 ),处理器状态。共有 4 种状态。状态变迁如下图:`handlerState` 变迁
HANDLER_STATE_UPDATER
静态属性,handlerState
的原子更新器。
4.1.2 构造方法
/** |
next
、prev
属性,分别记录上、下一个节点。- Handler 相关属性:
- 在 AbstractChannelHandlerContext 抽象类中,按照我们上文的分享,应该会看到一个类型为 ChannelHandler 的处理器,但是实际并不是这样。而是,😈 我们下文 DefaultChannelHandlerContext、TailContext、HeadContext 见。
inbound
、outbound
属性,分别是否为 Inbound、Outbound 处理器。name
属性,处理器名字。handlerState
属性,处理器状态,初始为INIT
。
executor
属性,EventExecutor 对象ordered
属性,是否使用有序的executor
,即 OrderedEventExecutor ,在构造方法的<1>
处理的初始化。
pipeline
属性,所属 DefaultChannelPipeline 对象。
4.1.3 setAddComplete
#setAddComplete()
方法,设置 ChannelHandler 添加完成。完成后,状态有两种结果:
REMOVE_COMPLETE
ADD_COMPLETE
代码如下:
final void setAddComplete() { |
- 循环 + CAS 保证多线程下的安全变更
handlerState
属性。
4.1.4 setRemoved
#setRemoved()
方法,设置 ChannelHandler 已移除。代码如下:
final void setRemoved() { |
4.1.5 setAddPending
#setAddPending()
方法,设置 ChannelHandler 准备添加中。代码如下:
final void setAddPending() { |
- 当且仅当
INIT
可修改为ADD_PENDING
。理论来说,这是一个绝对会成功的操作,原因见英文注释。
4.1.6 其他方法
AbstractChannelHandlerContext 中的其他方法,详细解析,见后续的文章。
4.2 HeadContext
HeadContext ,实现 ChannelOutboundHandler、ChannelInboundHandler 接口,继承 AbstractChannelHandlerContext 抽象类,pipe 头节点 Context 实现类。
HeadContext 是 DefaultChannelPipeline 的内部类。
4.2.1 构造方法
private final Unsafe unsafe; |
<1>
处,调用父 AbstractChannelHandlerContext 的构造方法,设置inbound = false
、outbound = true
。<2>
处,使用 Channel 的 Unsafe 作为unsafe
属性。HeadContext 实现 ChannelOutboundHandler 接口的方法,都会调用 Unsafe 对应的方法。代码如下:
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
unsafe.bind(localAddress, promise);
}
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
unsafe.connect(remoteAddress, localAddress, promise);
}
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
unsafe.disconnect(promise);
}
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
unsafe.close(promise);
}
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
unsafe.deregister(promise);
}
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
unsafe.write(msg, promise);
}
public void flush(ChannelHandlerContext ctx) throws Exception {
unsafe.flush();
}- 这也就是为什么设置
outbound = true
的原因。
- 这也就是为什么设置
<3>
处,调用#setAddComplete()
方法,设置 ChannelHandler 添加完成。此时,handlerStatus
会变成ADD_COMPLETE
状态。
4.2.2 handler
#handler()
方法,返回自己作为 Context 的 ChannelHandler 。代码如下:
|
- 因为 HeadContext ,实现 ChannelOutboundHandler、ChannelInboundHandler 接口,而它们本身就是 ChannelHandler 。
4.2.3 其他方法
HeadContext 中的其他方法,详细解析,见后续的文章。
4.3 TailContext
TailContext ,实现 ChannelInboundHandler 接口,继承 AbstractChannelHandlerContext 抽象类,pipe 尾节点 Context 实现类。
TailContext 是 DefaultChannelPipeline 的内部类。
4.3.1 构造方法
TailContext(DefaultChannelPipeline pipeline) { |
<1>
处,调用父 AbstractChannelHandlerContext 的构造方法,设置inbound = true
、outbound = false
,和 HeadContext 相反。<2>
处,调用#setAddComplete()
方法,设置 ChannelHandler 添加完成。此时,handlerStatus
会变成ADD_COMPLETE
状态。
4.3.2 handler
#handler()
方法,返回自己作为 Context 的 ChannelHandler 。代码如下:
|
- 因为 HeadContext ,实现 ChannelInboundHandler 接口,而它们本身就是 ChannelHandler 。
4.3.3 其他方法
TailContext 中的其他方法,详细解析,见后续的文章。
4.4 DefaultChannelHandlerContext
io.netty.channel.DefaultChannelHandlerContext
,实现 AbstractChannelHandlerContext 抽象类。代码如下:
final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext { |
- 不同于 HeadContext、TailContext,它们自身就是一个 Context 的同时,也是一个 ChannelHandler 。而 DefaultChannelHandlerContext 是内嵌 一个 ChannelHandler 对象,即
handler
。这个属性通过构造方法传入,在<2>
处进行赋值。 <1>
处,调用父 AbstractChannelHandlerContext 的构造方法,通过判断传入的handler
是否为 ChannelInboundHandler 和 ChannelOutboundHandler 来分别判断是否为inbound
和outbound
。
666. 彩蛋
推荐阅读如下文章: