基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

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

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 事件。
  • 事件传播到 HeadContextchannelActive 方法中。
  • HeadContext 会调用 readIfIsAutoRead(),进而调用 AbstractUnsafe.beginRead()

7. 注册 OP_ACCEPT 事件 (doBeginRead)

  • 这是服务端启动的最后一步,也是最关键的一步。
  • doBeginRead() 方法中,Netty 会获取之前注册 Selector 返回的 SelectionKey
  • 将其 interestOps 加上 SelectionKey.OP_ACCEPT
  • 至此,ServerSocketChannel 真正开始监听客户端的连接请求。服务端启动圆满完成。

总结与常见面试题解答

  1. 服务端 Channel 是什么?
    • NioServerSocketChannel,它封装了 JDK 的 ServerSocketChannel
  2. ServerBootstrapAcceptor 的作用是什么?什么时候添加的?
    • 作用:负责将接收到的新连接 (NioSocketChannel) 分配并注册到 Worker 线程池中。
    • 添加时机:在 init() 阶段准备,在 Channel 成功 Register 到 Selector 后正式添加到 Pipeline 中。
  3. Netty 服务端在什么时候才真正监听端口(注册 OP_ACCEPT)?
    • 不是在 Register 阶段! Register 阶段仅仅是将 Channel 挂载到 Selector 上(interestOps = 0)。
    • 是在 Bind 成功之后的 Active 阶段! 绑定端口成功后,触发 ChannelActive 事件,在 doBeginRead 中才会真正把 OP_ACCEPT 注册到 Selector 上。这种设计保证了在完全准备好接收请求之前,不会有连接进来。
00:00
00:00