Netty 服务端启动的核心流程(从 ServerBootstrap.bind() 开始)
Netty 服务端的启动流程是 Netty 核心源码中最经典的部分之一。从 ServerBootstrap.bind(port) 开始,整个过程是高度异步化的,并且涉及主线程(Main Thread)和 Boss 线程(Boss EventLoop)之间的任务切换。
我们可以将整个核心流程划分为三大阶段:创建与初始化(Init)、注册(Register)、绑定与激活(Bind & Active)。
以下是核心流程的深度剖析:
核心流程全景图
ServerBootstrap.bind()
└── doBind()
├── 1. initAndRegister() (创建 + 初始化 + 注册)
│ ├── newChannel() -> 创建 NioServerSocketChannel
│ ├── init(channel) -> 初始化(添加 ServerBootstrapAcceptor)
│ └── group().register() -> 将 Channel 注册到 Boss EventLoop 的 Selector
│
└── 2. doBind0() (底层端口绑定)
├── channel.bind() -> 调用 JDK 底层进行端口绑定
└── pipeline.fireChannelActive() -> 触发 Active 事件
└── doBeginRead() -> 注册 OP_ACCEPT 事件
第一阶段:创建与初始化 (initAndRegister - 前半部分)
执行线程: 通常是 Main 主线程
当调用 bind(port) 时,内部最终会调用到 doBind(final SocketAddress localAddress)。第一步就是执行 initAndRegister()。
1. 创建 Channel (newChannel)
- 通过反射工厂(
ReflectiveChannelFactory)调用NioServerSocketChannel的无参构造函数。 - 底层动作:
- 调用 JDK 的
SelectorProvider.openServerSocketChannel()创建底层的 JDK Channel。 - 将 JDK Channel 配置为非阻塞模式 (
ch.configureBlocking(false))。 - 为该 Channel 创建唯一标识
ChannelId、底层的读写组件Unsafe,以及绑定的职责链ChannelPipeline。
- 调用 JDK 的
2. 初始化 Channel (init)
- 设置 Channel 的 Options(如
SO_BACKLOG)和 Attributes。 - 核心动作:向 Pipeline 添加 Acceptor
- Netty 会向 ServerSocketChannel 的 Pipeline 中添加一个初始化 Handler。
- 这个 Handler 的作用是在 Channel 注册成功后,向 Pipeline 中真正添加
ServerBootstrapAcceptor。 - 注:
ServerBootstrapAcceptor是一个特殊的入站处理器,它的职责是接收客户端新连接(SocketChannel),并将它们注册到 Worker EventLoopGroup 中。
第二阶段:注册 (initAndRegister - 后半部分)
执行线程: 从 Main 线程切换到 Boss EventLoop 线程
3. 注册到 Selector (group().register)
- 调用 Boss EventLoopGroup 的
register(channel)方法。 - 最终会调用到底层组件
AbstractUnsafe.register()。 - 线程切换: 此时 Netty 会检查当前线程是否是 Boss EventLoop 线程。因为当前是 Main 线程,所以 Netty 会将注册逻辑封装成一个 Task 提交给 Boss EventLoop 异步执行。后续的所有逻辑都在 Boss 线程中运行。
4. 实际注册 (doRegister)
- 在 Boss 线程中,调用 JDK 的
java.nio.channels.SelectableChannel.register(selector, 0, this)。 - 关键点: 注意这里的
ops传入的是0。这意味着此时 Channel 已经绑定到了 Selector 上,但还没有监听任何事件(没有监听OP_ACCEPT)。 - 注册成功后,触发 Pipeline 的
fireChannelRegistered事件。
第三阶段:绑定与激活 (doBind0)
执行线程: Boss EventLoop 线程
当 initAndRegister 异步完成后,会回调执行 doBind0,开始真正的端口绑定。
5. 绑定端口 (channel.bind)
- 调用通过 Pipeline 传播 Bind 事件,最终由
HeadContext传递给底层的AbstractUnsafe.bind()。 - 调用
doBind(),底层执行 JDK 的java.nio.channels.ServerSocketChannel.bind(address, backlog),完成真正的 OS 层面端口绑定。
6. 激活 Channel (fireChannelActive)
- 端口绑定成功后,通道处于活跃状态,Netty 会在 Pipeline 中触发
fireChannelActive事件。 - 事件传播到
HeadContext的channelActive方法中。 HeadContext会调用readIfIsAutoRead(),进而调用AbstractUnsafe.beginRead()。
7. 注册 OP_ACCEPT 事件 (doBeginRead)
- 这是服务端启动的最后一步,也是最关键的一步。
- 在
doBeginRead()方法中,Netty 会获取之前注册 Selector 返回的SelectionKey。 - 将其
interestOps加上SelectionKey.OP_ACCEPT。 - 至此,ServerSocketChannel 真正开始监听客户端的连接请求。服务端启动圆满完成。
总结与常见面试题解答
- 服务端 Channel 是什么?
- 是
NioServerSocketChannel,它封装了 JDK 的ServerSocketChannel。
- 是
ServerBootstrapAcceptor的作用是什么?什么时候添加的?- 作用:负责将接收到的新连接 (NioSocketChannel) 分配并注册到 Worker 线程池中。
- 添加时机:在
init()阶段准备,在 Channel 成功 Register 到 Selector 后正式添加到 Pipeline 中。
- Netty 服务端在什么时候才真正监听端口(注册
OP_ACCEPT)?- 不是在 Register 阶段! Register 阶段仅仅是将 Channel 挂载到 Selector 上(interestOps = 0)。
- 是在 Bind 成功之后的 Active 阶段! 绑定端口成功后,触发
ChannelActive事件,在doBeginRead中才会真正把OP_ACCEPT注册到 Selector 上。这种设计保证了在完全准备好接收请求之前,不会有连接进来。