5.Channel 的生命周期

0 阅读14分钟

Channel 的生命周期

本文以 Netty 4.2 NIO 实现为背景,把 Channel 从创建、注册、激活、读写到关闭和注销的过程串成一条完整主线。

本文负责建立生命周期的整体地图,具体通信流程不重复展开。相关专题如下:

  • 《管理端注册》:服务端 EventLoopGroupServerBootstrap 和服务端监听 Channel 的初始化。
  • 《客户端注册》:客户端 Channel 的创建、注册和连接。
  • 《客户端向服务端建立连接》:客户端 connect()、服务端 accept() 和服务端子 Channel 注册。
  • 《客户端发送服务端接收》:Channel 激活期间的写出、读取、编码和解码。

1. 先区分三种 Channel

一次 TCP 通信至少涉及三种 Channel,它们处于不同进程,也拥有各自的 Pipeline 和 EventLoop。

1.1 服务端监听 Channel

  • Netty 类型:NioServerSocketChannel
  • 创建位置:服务端 ServerBootstrap
  • 主要职责:绑定本地端口、监听并接收客户端连接。
  • 激活条件:底层 ServerSocketChannel 已经绑定本地端口。

1.2 客户端 Channel

  • Netty 类型:NioSocketChannel
  • 创建位置:客户端 Bootstrap
  • 主要职责:主动连接服务端,并与服务端进行数据读写。
  • 激活条件:底层 SocketChannel 已经建立连接。

1.3 服务端子 Channel

  • Netty 类型:NioSocketChannel
  • 创建位置:服务端执行 accept() 时。
  • 主要职责:表示服务端一侧的一条客户端连接。
  • 激活条件:accept() 返回的底层 SocketChannel 已经建立连接。

需要特别注意:

  • 客户端 Channel 和服务端子 Channel 表示同一条 TCP 连接的两端,但不是同一个对象。
  • 服务端监听 Channel 通常注册到 boss EventLoopGroup
  • 服务端子 Channel 通常注册到 worker EventLoopGroup
  • 一个 Channel 只拥有一个 Pipeline,并在注册后绑定一个 EventLoop。

2. Channel 的三个状态

理解生命周期不能只看 channelActive,还要同时观察 isOpen()isRegistered()isActive()

2.1 isOpen()

表示底层 JDK Channel 是否仍然打开。

NioServerSocketChannel.isOpen()
NioSocketChannel.isOpen()
    -> javaChannel().isOpen()

Channel 创建后通常满足 isOpen() == true;调用 doClose() 关闭底层 JDK Channel 后变为 isOpen() == false

2.2 isRegistered()

表示 Netty Channel 是否已经注册到某个 EventLoop 的 I/O 处理器中。

该状态由 AbstractChannel.registered 维护:

doRegister 成功 -> registered = true
doDeregister 完成 -> registered = false

注册不仅是“放进 Selector”,还建立了 Channel 与 EventLoop 的线程归属关系。

2.3 isActive()

表示 Channel 是否已经具备实际 I/O 能力。不同 Channel 的判断条件不同:

// NioServerSocketChannel
public boolean isActive() {
    return isOpen() && javaChannel().socket().isBound();
}

// NioSocketChannel
public boolean isActive() {
    SocketChannel ch = javaChannel();
    return ch.isOpen() && ch.isConnected();
}

2.4 三类 Channel 的状态变化

因此,isRegistered()isActive() 表示两个相互独立的状态:

  • isRegistered() 关注 Channel 是否已经纳入 EventLoop 的 I/O 调度。
  • isActive() 关注底层 Channel 是否已经具备实际通信能力。

三类 Channel 的典型状态变化如下:

服务端监听 Channel
    创建:isOpen() = true,isRegistered() = false,isActive() = false
    注册:isOpen() = true,isRegistered() = true,isActive() = false
    绑定:isOpen() = true,isRegistered() = true,isActive() = true

客户端 Channel
    创建:isOpen() = true,isRegistered() = false,isActive() = false
    注册:isOpen() = true,isRegistered() = true,isActive() = false
    连接:isOpen() = true,isRegistered() = true,isActive() = true

服务端子 Channel
    accept() 后:isOpen() = true,isRegistered() = false,isActive() = true
    注册后:isOpen() = true,isRegistered() = true,isActive() = true

3. 生命周期总览

3.1 服务端监听 Channel

创建 NioServerSocketChannel
    -> 初始化 Pipeline、ChannelOption、Attribute
    -> 注册到 boss EventLoop
    -> handlerAdded()
    -> channelRegistered()
    -> bind() 本地端口
    -> channelActive()
    -> 开始监听 ACCEPT
    -> 持续执行 accept(),创建服务端子 Channel
    -> close()
    -> doDeregister()
    -> channelInactive()
    -> channelUnregistered()
    -> 移除 Pipeline 中的 Handler

3.2 客户端 Channel

创建 NioSocketChannel
    -> 初始化 Pipeline、ChannelOption、Attribute
    -> 注册到客户端 EventLoop
    -> handlerAdded()
    -> channelRegistered()
    -> connect() 服务端
    -> 立即连接成功,或者等待 CONNECT 就绪
    -> channelActive()
    -> read() / write()
    -> 主动 close() 或收到对端 FIN
    -> doDeregister()
    -> channelInactive()
    -> channelUnregistered()

3.3 服务端子 Channel

服务端监听 Channel 收到 ACCEPT 就绪
    -> accept 得到 JDK SocketChannel
    -> 创建 NioSocketChannel,此时 isActive() = true
    -> 添加 childHandler、childOptions、childAttrs
    -> 注册到 worker EventLoop
    -> handlerAdded()
    -> channelRegistered()
    -> channelActive()
    -> read() / write()
    -> 主动 close() 或收到对端 FIN
    -> doDeregister()
    -> channelInactive()
    -> channelUnregistered()

4. Channel 的创建与初始化

服务端 bind() 和客户端 connect() 都会先进入 AbstractBootstrap.initAndRegister()

AbstractBootstrap.initAndRegister()
    -> channelFactory.newChannel()
    -> init(channel)
    -> config().group().register(channel)

它完成三件事:

  1. 创建 Channel。
  2. 初始化 Channel 配置和 Pipeline。
  3. 把 Channel 注册到 EventLoop。

4.1 创建服务端监听 Channel

服务端通常创建 NioServerSocketChannel,构造器链路是:

NioServerSocketChannel
    -> AbstractNioMessageChannel
    -> AbstractNioChannel
    -> AbstractChannel

创建过程中的关键动作:

SelectorProvider.openServerSocketChannel()
    -> 创建底层 JDK ServerSocketChannel
    -> configureBlocking(false)
    -> 保存 readOps = ACCEPT
    -> 创建 ChannelId
    -> 创建 Unsafe
    -> 创建 DefaultChannelPipeline
    -> 创建 ChannelConfig

SelectorProvider 是创建 JDK NIO 对象的提供者(provider),不是 Selector 本身。

4.2 AbstractChannel 初始化的核心对象

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}
  • ChannelId:Channel 的唯一标识。
  • Unsafe:执行注册、bind、connect、read、write、close 等底层操作。
  • Pipeline:传播生命周期事件和 I/O 事件。
  • parent:服务端监听 Channel 和客户端 Channel 通常为 null;服务端子 Channel 的 parent 是服务端监听 Channel。

4.3 服务端和客户端初始化的差异

ServerBootstrap.init(channel) 初始化服务端监听 Channel:

设置服务端 ChannelOption / Attribute
    -> 添加服务端 Handler
    -> 添加 ServerBootstrapAcceptor

ServerBootstrapAcceptor 负责处理 accept() 创建的服务端子 Channel,并使用 childHandlerchildOptionschildAttrs 初始化它。

Bootstrap.init(channel) 初始化客户端 Channel:

设置 ChannelOption / Attribute
    -> 将用户配置的 ChannelInitializer 加入 Pipeline

客户端不存在 childGroupServerBootstrapAcceptor

5. 注册到 EventLoop

注册主链路如下:

EventLoopGroup.register(channel)
    -> chooser.next() 选择 EventLoop
    -> SingleThreadEventLoop.register(channel)
    -> AbstractChannel.AbstractUnsafe.register(eventLoop, promise)
    -> register0(promise)
    -> AbstractNioChannel.doRegister(registerPromise)
    -> IoEventLoop.register(unsafe)
    -> 底层注册完成

5.1 为什么 register0() 必须在 EventLoop 线程执行

if (eventLoop.inEventLoop()) {
    register0(promise);
} else {
    eventLoop.execute(() -> register0(promise));
}

这样做的主要目的不是依靠线程切换直接保证所有业务数据可见,而是建立清晰的线程归属:

  • Selector 注册和 I/O 事件修改由对应 EventLoop 串行处理。
  • 同一个 Channel 的 I/O 回调和默认 Pipeline 回调通常在同一个 EventLoop 线程执行。
  • 外部线程调用 Channel API 时,实际 I/O 操作会被调度回 EventLoop。
  • 减少对 Channel 内部状态加锁的需求。

如果某个 Handler 显式绑定了其他 EventExecutorGroup,它的回调可能在其他线程执行,业务共享状态仍然需要自行保证线程安全,netty为什么要这样做?

如果一个handler中的代码中包含比较耗时的任务,可能会出现这个任务耗时过长然后其他的所有需要这个线程的任务堵塞的情况

5.2 注册成功后的事件

register0() 会等待底层注册 Promise 完成:

doRegister 成功
    -> neverRegistered = false
    -> registered = true
    -> pipeline.invokeHandlerAddedIfNeeded()
    -> 注册 Promise 成功
    -> pipeline.fireChannelRegistered()
    -> 如果首次注册并且 isActive(),触发 channelActive

关键代码可以简化为:

registerPromise.addListener(future -> {
    if (future.isSuccess()) {
        neverRegistered = false;
        registered = true;
        pipeline.invokeHandlerAddedIfNeeded();
        safeSetSuccess(promise);
        pipeline.fireChannelRegistered();

        if (isActive()) {
            if (firstRegistration) {
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                beginRead();
            }
        }
    }
});

这里不能简单理解成“注册完成必然触发 channelActive”:

  • 服务端监听 Channel 注册时尚未执行 bind(),因此只触发 channelRegistered
  • 客户端 Channel 注册时通常尚未执行 connect(),因此只触发 channelRegistered
  • accept() 创建的服务端子 Channel 已经连接,所以首次注册后会继续触发 channelActive

6. Channel 的三种激活方式

6.1 服务端监听 Channel:bind() 后激活

服务端绑定链路:

AbstractBootstrap.doBind()
    -> initAndRegister()
    -> AbstractBootstrap.doBind0()
    -> channel.bind()
    -> Pipeline 出站传播 bind
    -> DefaultChannelPipeline.HeadContext.bind()
    -> AbstractChannel.AbstractUnsafe.bind()
    -> NioServerSocketChannel.doBind()
    -> pipeline.fireChannelActive()

底层绑定:

protected void doBind(SocketAddress localAddress) throws Exception {
    javaChannel().bind(localAddress, config.getBacklog());
}

doBind() 完成后,ServerSocketChannel 已经绑定端口,isActive()false 变为 true

boolean wasActive = isActive();
doBind(localAddress);

if (!wasActive && isActive()) {
    invokeLater(() -> pipeline.fireChannelActive());
}
safeSetSuccess(promise);

这里有两个重要顺序:

  1. 服务端监听 Channel 先触发 channelRegistered,执行 bind() 后才触发 channelActive
  2. channelActive 通过 invokeLater() 延后执行,因此 bind Promise 先完成,然后 Handler 才收到 channelActive

HeadContext.channelActive() 在传播事件后会检查 autoRead

fireChannelActive
    -> 用户 Handler.channelActive()
    -> HeadContext.readIfIsAutoRead
    -> channel.read()
    -> AbstractUnsafe.beginRead()
    -> AbstractNioChannel.doBeginRead()
    -> 开始关注 ACCEPT

完整服务端启动过程参见《管理端注册》。

6.2 客户端 Channel:connect() 后激活

客户端 Channel 注册完成后才发起 connect()

Bootstrap.connect()
    -> initAndRegister()
    -> doResolveAndConnect0()
    -> Bootstrap.doConnect()
    -> channel.connect()
    -> Pipeline 出站传播 connect
    -> HeadContext.connect()
    -> AbstractNioUnsafe.connect()
    -> NioSocketChannel.doConnect()

非阻塞连接有两条分支。

立即成功:

doConnect() 返回 true
    -> fulfillConnectPromise()
    -> connect Promise 成功
    -> pipeline.fireChannelActive()

异步完成:

doConnect() 返回 false
    -> 保存 connectPromise
    -> 注册 CONNECT 兴趣事件
    -> EventLoop.select()
    -> CONNECT 就绪
    -> AbstractNioUnsafe.handle()
    -> finishConnect()
    -> NioSocketChannel.doFinishConnect()
    -> fulfillConnectPromise()
    -> connect Promise 成功
    -> pipeline.fireChannelActive()

connectTimeoutFuture 只负责连接超时失败,不负责把连接推进到成功状态。异步连接成功依赖 Selector 的 CONNECT 就绪事件。

完整实现参见《客户端注册》和《客户端向服务端建立连接》。

6.3 服务端子 Channel:accept() 时已经激活

服务端监听 Channel 收到 ACCEPT 就绪事件后:

NioIoHandler.processSelectedKey()
    -> AbstractNioUnsafe.handle()
    -> AbstractNioMessageChannel.NioMessageUnsafe.read()
    -> NioServerSocketChannel.doReadMessages()
    -> JDK ServerSocketChannel.accept()
    -> new NioSocketChannel(parent, acceptedSocketChannel)
    -> parentPipeline.fireChannelRead(child)
    -> ServerBootstrapAcceptor.channelRead(child)

NioServerSocketChannel.doReadMessages() 的核心逻辑:

SocketChannel ch = SocketUtils.accept(javaChannel());
if (ch != null) {
    buf.add(new NioSocketChannel(this, ch));
    return 1;
}
6.3.1 为什么 accept() 返回的 Channel 已经连接

这里需要把“TCP 已连接”和“注册到 Netty EventLoop”分开理解。

客户端完成 TCP 三次握手后,服务端内核会把连接放入已完成连接队列。服务端监听 Channel 收到 ACCEPT 就绪事件后,ServerSocketChannel.accept() 从队列中取出一条连接:

  • 非阻塞模式下,如果当前没有可接收的连接,accept() 可能返回 null
  • 只要返回值不为 null,得到的 JDK SocketChannel 就代表一条已经建立的服务端连接,不需要再次执行 connect()
6.3.2 包装成 Netty Channel 后的状态

Netty 随后执行:

new NioSocketChannel(this, ch)

这一步只是用 Netty 的 NioSocketChannel 包装已连接的 JDK SocketChannel,并完成以下初始化:

设置 parent = NioServerSocketChannel
    -> 保存底层 JDK SocketChannel
    -> 设置为非阻塞模式
    -> 设置 readOps = READ
    -> 创建 Unsafe、Pipeline 和 ChannelConfig

此时 NioSocketChannel.isActive() 会直接检查底层连接状态:

public boolean isActive() {
    SocketChannel ch = javaChannel();
    return ch.isOpen() && ch.isConnected();
}

因为 accept() 返回的 SocketChannel 同时处于打开和已连接状态,所以服务端子 Channel 创建后已经满足:

isOpen()       = true
isActive()     = true
isRegistered() = false

isRegistered() == false 是因为创建和包装 Channel 不会自动执行 childGroup.register(child)。此时服务端子 Channel 还没有:

  • 从 worker EventLoopGroup 中选出自己的 EventLoop。
  • 建立 Channel 与 worker EventLoop 的线程归属关系。
  • 创建底层 IoRegistration,并加入 worker EventLoop 的 I/O 监听。
  • 调用子 Pipeline 中待执行的 handlerAddedchannelRegistered

因此,这个短暂阶段可以理解为:TCP 连接在操作系统层面已经建立,但尚未接入 Netty 的 worker EventLoop 调度体系,暂时不能进入正常的 Pipeline 读写流程。

6.3.3 注册到 worker EventLoop

ServerBootstrapAcceptor 随后完成:

添加 childHandler
    -> 设置 childOptions
    -> 设置 childAttrs
    -> childGroup.register(child)

所以服务端子 Channel 首次注册成功后的事件顺序是:

handlerAdded()
    -> channelRegistered()
    -> channelActive()

注册成功后,register0() 检查到服务端子 Channel 已经满足 isActive() == true,于是触发首次 channelActive。这里的 channelActive 不是把状态从 false 修改成 true,而是通知 Pipeline:服务端子 Channel 的底层连接已经可用,并且现在也完成了 EventLoop 注册,可以开始正常处理读写事件。

完整时间线如下:

TCP 三次握手完成
    -> 连接进入服务端已完成连接队列
    -> ServerSocketChannel.accept()
    -> 得到已连接的 JDK SocketChannel
    -> 包装成 Netty NioSocketChannel
       isActive() = true,isRegistered() = false
    -> ServerBootstrapAcceptor 初始化子 Pipeline 和配置
    -> childGroup.register(child)
    -> 注册到 worker EventLoop
       isActive() = true,isRegistered() = true
    -> channelRegistered()
    -> channelActive()
    -> autoRead 开启时开始关注 READ 事件

这里的 channelActive 表示服务端子 Channel 可以开始读写,不表示服务端监听 Channel 再次激活,也不会触发客户端 Pipeline 中的 channelActive

7. 激活期间的读写

Netty 4.2 使用 NioIoOps 封装 JDK SelectionKey 的事件位。它不是写任务或网络消息的计数器,而是一组 I/O 事件状态:

NONE:Channel 已注册,但暂未监听 I/O
ACCEPT:服务端监听 Channel 等待接收连接
CONNECT:客户端 Channel 等待异步连接完成
READ:连接 Channel 等待读取数据
WRITE:连接 Channel 等待底层恢复可写

其中,interestOps 表示 Channel 希望 Selector 监听哪些事件,readyOps 表示 Selector 本次检测到哪些事件已经就绪。一个 NioIoOps 对象可以通过位运算同时表示多个事件,例如 READ_AND_WRITE。

Channel 注册时从 NONE 开始;激活并开启 autoRead 后,服务端监听 Channel 添加 ACCEPT,连接 Channel 添加 READ;异步连接期间临时添加 CONNECT;数据没有写完时临时添加 WRITE,写完后立即移除 WRITE。addAndSubmit()removeAndSubmit() 最终通过 SelectionKey.interestOps(...) 修改 Selector 关注的事件。

channelActive 传播到 HeadContext 后,如果开启 autoRead,Netty 会调用 channel.read(),把 READ 或 ACCEPT 加入 interestOps

7.1 写数据

业务 Handler.writeAndFlush(msg)
    -> Pipeline 出站传播
    -> 编码器把业务对象转换成 ByteBuf
    -> HeadContext.write()
    -> AbstractUnsafe.write()
    -> ChannelOutboundBuffer.addMessage()
    -> AbstractUnsafe.flush()
    -> ChannelOutboundBuffer.addFlush()
    -> AbstractUnsafe.flush0()
    -> NioSocketChannel.doWrite()
    -> JDK SocketChannel.write()

write 只把消息放入 ChannelOutboundBufferflush 才会把消息标记为可写并尝试写入操作系统 socket 缓冲区。

7.2 读数据

Selector 返回 READ 就绪
    -> AbstractNioUnsafe.handle()
    -> AbstractNioByteChannel.NioByteUnsafe.read()
    -> NioSocketChannel.doReadBytes()
    -> pipeline.fireChannelRead(ByteBuf)
    -> 解码器
    -> 业务 Handler.channelRead(msg)
    -> pipeline.fireChannelReadComplete()

读写不会产生新的 Channel 生命周期,但以下情况会推动生命周期进入关闭阶段:

  • 主动调用 channel.close()
  • 读到 EOF,即 SocketChannel.read() 返回 -1
  • 连接、读写或 Pipeline 处理发生不可恢复异常。
  • EventLoop 关闭并清理已注册 Channel。

完整读写链路参见《客户端发送服务端接收》。

8. 关闭与注销

8.1 主动关闭链路

close 是出站事件,从当前 Context 向 Head 方向传播:

channel.close()
    -> DefaultChannelPipeline.close()
    -> AbstractChannelHandlerContext.close()
    -> DefaultChannelPipeline.HeadContext.close()
    -> AbstractChannel.AbstractUnsafe.close()
    -> doClose0()
    -> 具体 Channel.doClose()
    -> fireChannelInactiveAndDeregister()
    -> deregister()
    -> doDeregister()
    -> channelInactive()
    -> channelUnregistered()

对于 NIO Channel,链路中不会经过 EmbeddedChannel.EmbeddedUnsafeEmbeddedUnsafe 只属于测试用的 EmbeddedChannel

8.2 close() 如何处理底层资源

AbstractUnsafe.close() 首先阻止新的写入,然后关闭底层 Channel:

closeInitiated = true
    -> 保存并清空 outboundBuffer 引用
    -> doClose0(promise)
    -> doClose()
    -> closeFuture.setClosed()
    -> close Promise 完成
    -> 失败通知并释放 outboundBuffer 中剩余消息

NioSocketChannel.doClose() 还会先处理尚未完成的连接操作:

AbstractNioChannel.doClose()
    -> 连接 Promise 失败
    -> 取消连接超时任务
    -> javaChannel().close()

服务端监听 Channel 则直接关闭底层 ServerSocketChannel

8.3 为什么关闭后还要注销

关闭解决的是底层网络资源;注销解决的是 EventLoop 的注册关系。

private void fireChannelInactiveAndDeregister(boolean wasActive) {
    deregister(voidPromise(), wasActive && !isActive());
}

实际注销通过 invokeLater() 延后执行,避免在 Pipeline 正在处理事件时立刻改变 EventLoop 归属,导致同一个 Handler 被不同 EventLoop 并发调用。

doDeregister()
    -> AbstractNioChannel 取消 IoRegistration
    -> 如有需要,fireChannelInactive()
    -> registered = false
    -> fireChannelUnregistered()

HeadContext.channelUnregistered() 发现 Channel 已关闭时,会销毁 Pipeline,依次移除 Handler 并触发 handlerRemoved

8.4 对端关闭如何进入同一条链路

TCP 对端正常关闭时,本端通常在读事件中发现 EOF:

NioByteUnsafe.read()
    -> NioSocketChannel.doReadBytes()
    -> 返回 -1
    -> closeOnRead()
    -> AbstractUnsafe.close()
    -> channelInactive()
    -> channelUnregistered()

如果启用了 ALLOW_HALF_CLOSURE,收到 FIN 后可能只关闭输入方向并触发 ChannelInputShutdownEvent,不会立即完整关闭 Channel。

8.5 close()deregister() 的区别

close() 负责终止 Channel:

  • 关闭底层 JDK Channel。
  • Channel 原来处于激活状态时触发 channelInactive
  • 注销完成后触发 channelUnregistered
  • 关闭后的 Channel 不能重新注册和复用。

deregister() 只负责解除注册关系:

  • 不关闭底层 JDK Channel。
  • 通常不触发 channelInactive
  • 注销完成后触发 channelUnregistered
  • Channel 仍然打开时,可以重新注册到兼容的 EventLoop。

因此,deregister() 不能代替 close() 关闭网络连接。

8.6 closeFuture 完成不代表注销事件已经执行完

doClose0() 中先执行:

doClose()
    -> closeFuture.setClosed()
    -> close Promise 成功

随后才执行 channelInactive 和异步 deregister。因此:

  • channel.close().sync() 主要表示关闭操作完成。
  • channel.closeFuture().sync() 表示底层 Channel 已关闭。
  • 如果测试需要确认 channelInactivechannelUnregistered 已经执行,应在 Handler 中使用 Promise、队列或 CountDownLatch 等待事件,不应只等待 closeFuture

9. 生命周期事件顺序

9.1 服务端监听 Channel

handlerAdded()
    -> channelRegistered()
    -> bind Promise 成功
    -> channelActive()
    -> channelInactive()
    -> channelUnregistered()
    -> handlerRemoved()

9.2 客户端 Channel

handlerAdded()
    -> channelRegistered()
    -> connect Promise 成功
    -> channelActive()
    -> channelRead() / channelReadComplete() / write()
    -> channelInactive()
    -> channelUnregistered()
    -> handlerRemoved()

9.3 服务端子 Channel

accept() 后已经 isActive() = true
    -> handlerAdded()
    -> channelRegistered()
    -> channelActive()
    -> channelRead() / channelReadComplete() / write()
    -> channelInactive()
    -> channelUnregistered()
    -> handlerRemoved()

事件顺序描述的是默认 NIO 流程。

10. Promise 与 Future 在生命周期中的作用

Channel 的状态变化大多由异步操作驱动。Promise 用于设置操作结果,Future 用于观察操作是否完成以及成功或失败。

生命周期中常见的 Future 包括:

  • register Future:表示 Channel 是否成功注册到 EventLoop。
  • bind Future:表示服务端监听 Channel 是否成功绑定本地地址。
  • connect Future:表示客户端 Channel 是否成功建立连接。
  • write Future:表示对应消息是否成功从 Netty 出站缓冲区写出。
  • closeFuture:表示底层 Channel 是否已经关闭。

DefaultPromise 通过 CAS 设置完成状态,再通知监听器。监听器默认由 Promise 关联的 EventExecutor 执行:

promise.setSuccess()
    -> setSuccess0()
    -> setValue0()
    -> notifyListeners()
    -> listener.operationComplete()

Promise 完成和生命周期事件传播属于两个概念。例如,bind Promise 或 connect Promise 可以先完成,随后再触发 channelActivecloseFuture 也可以先完成,随后再触发注销事件。

11. 推荐源码阅读顺序

第一阶段,创建和注册:

AbstractBootstrap.initAndRegister
ServerBootstrap.init / Bootstrap.init
AbstractChannel.AbstractUnsafe.register
AbstractChannel.AbstractUnsafe.register0
AbstractNioChannel.doRegister
ChannelInitializer.handlerAdded / initChannel

第二阶段,三种激活方式:

AbstractBootstrap.doBind0
AbstractChannel.AbstractUnsafe.bind
NioServerSocketChannel.doBind

AbstractNioChannel.AbstractNioUnsafe.connect
NioSocketChannel.doConnect
AbstractNioChannel.AbstractNioUnsafe.finishConnect

AbstractNioMessageChannel.NioMessageUnsafe.read
NioServerSocketChannel.doReadMessages
ServerBootstrap.ServerBootstrapAcceptor.channelRead

第三阶段,激活期间的 I/O:

NioIoOps
DefaultChannelPipeline.HeadContext.channelActive
AbstractNioChannel.doBeginRead
AbstractNioChannel.addAndSubmit / removeAndSubmit
AbstractNioChannel.AbstractNioUnsafe.handle
AbstractNioByteChannel.NioByteUnsafe.read
NioSocketChannel.doReadBytes
AbstractChannel.AbstractUnsafe.write / flush0
NioSocketChannel.doWrite

第四阶段,关闭和注销:

DefaultChannelPipeline.HeadContext.close
AbstractChannel.AbstractUnsafe.close
AbstractChannel.AbstractUnsafe.doClose0
NioServerSocketChannel.doClose / NioSocketChannel.doClose
AbstractChannel.AbstractUnsafe.fireChannelInactiveAndDeregister
AbstractChannel.AbstractUnsafe.deregister
AbstractNioChannel.doDeregister
DefaultChannelPipeline.HeadContext.channelUnregistered

12. 最终理解

Channel 生命周期的核心不是一条固定的事件列表,而是三个状态在不同操作下发生变化:

创建 Channel 后,isOpen() = true
register() 决定 isRegistered() 和 EventLoop 归属
bind() / connect() / accept() 决定 isActive()
NioIoOps 决定 Selector 当前监听和分发哪些 I/O 事件
read() / write() 发生在激活期间
close() 让 isOpen()、isActive() 变为 false
deregister() 让 isRegistered() 变为 false
Pipeline 把这些状态变化通知给 Handler
Promise 与 Future 把异步操作结果通知给调用方

只要始终区分服务端监听 Channel、客户端 Channel 和服务端子 Channel,并同时观察 isOpen()isRegistered()isActive(),就能把 bind()connect()accept()read()write()close() 放回同一个生命周期模型中理解。