Netty_ServerBootstrap启动过程
2025-01-22 08:19:30    4.3k 字   
This post is also available in English and alternative languages.

了解 Netty 中 ServerBootstrap 的启动过程,本篇直接从 io.netty.bootstrap.AbstractBootstrap#doBind 开始,上层一些校验方法就忽略了。

示例代码(点击跳转) 中所示,Netty 服务端同时设置了 parentNioEventLoopGroup(主reactor, 以下简称 parentGroup) 和 childNioEventLoopGroup(从reactor, 以下简称 childGroup),即采用了 主从reactor-多线程模型


  • 网上很多资料都将创建的 NioEventLoopGroup 对象称之为: bossGroup(boss) 和 workGroup(work),这里OP不会这么称呼,而是采用源码中变量的命名,即: parentGroupchildGroup

  • 文中图片较大,懒加载,请耐心等候

  • 本文 Netty 源码解析基于 4.1.86.Final 版本。


1. doBind

doBind 中调用了两个重要方法,启动的核心逻辑就是在 initAndRegisterdoBind0 两个方法中完成的。

  • line: 6 -> initAndRegister 方法负责创建、初始化 NioServerSocketChannel ,并且将 NioServerSocketChannel 注册到 parentGroup 中某个 NioEventLoop 的 Selector(多路复用器)[1] 上。

  • line: 14 或 line: 25 -> doBind0 方法用于端口绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// io.netty.bootstrap.AbstractBootstrap#doBind

private ChannelFuture doBind(final SocketAddress localAddress) {

// 创建、初始化、注册 NioServerSocketChannel, 这是一个异步的过程
final ChannelFuture regFuture = initAndRegister();

// 此处的 channel 就是 NioServerSocketChannel, 由 serverBootstrap.channel 方法设置。
final Channel channel = regFuture.channel();
...
if (regFuture.isDone()) { // 注册完成并且成功
ChannelPromise promise = channel.newPromise();
// 绑定端口
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else { // 由于是异步,此时注册还没有完成,但总是会完成的
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
// 添加回调监听器
regFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {...} else {
promise.registered();
// 绑定端口
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}

2. initAndRegister

initAndRegister 方法分为三个步骤:

  1. line:6 -> 通过 ChannelFactory 创建 NioServerSocketChannel,由 serverBootstrap.channel 方法设置。
  2. line:8 -> 初始化 NioServerSocketChannel。
  3. line:12 -> 将 NioServerSocketChannel 注册到 parentGroup 中某个 NioEventLoop 的 Selector(多路复用器)[1] 上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
final ChannelFuture initAndRegister() {

// 此处的 channel,就是 NioServerSocketChannel,返回具体类型是由 serverBootstrap.channel 方法设置
Channel channel = null;
try {
channel = channelFactory.newChannel();
// 初始化 NioServerSocketChannel
init(channel);
} catch (Throwable t) {...}

// 将 NioServerSocketChannel 注册到 parentGroup 中某个 NioEventLoop 的 Selector(多路复用器) 上。
ChannelFuture regFuture = config().group().register(channel);
...
}

2.1. 创建Channel

channelFactory 属性是由 serverBootstrap.channel 方法设置的,在 Netty_浅看ServerBootstrap配置 的「channel方法」一节中介绍过,这里不再赘述。

结合 示例代码(点击跳转)constructor.newInstance() 方法调用的是 NioServerSocketChannel 的无参构造函数,创建了 NioServerSocketChannel 对象。

1
2
3
4
5
6
7
8
channel = channelFactory.newChannel();

// io.netty.channel.ReflectiveChannelFactory
public T newChannel() {
try {
return constructor.newInstance();
} catch (Throwable t) {...}
}

关于 NioServerSocketChannel,概括来说:NioServerSocketChannel 是非阻塞的, 并且关注 OP_ACCEPT 事件, 具体读写细节在内部类 NioMessageUnsafe 中,会依次处理客户端连接最后统一触发 ChannelRead 事件

具体内容详阅: Netty_NioServerSocketChannel与NioSocketChannel ,此处不再赘述。


2.2. 初始化Channel

初始化 NioServerSocketChannel 分三个部分: 配置TCP参数、配置自定义属性参数、装配 pipeline 流水线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void init(Channel channel) {

// 为 NioServerSocketChannel 配置TCP等参数
// newOptionsArray 方法返回的就是由 serverBootstrap.option 方法添加的参数
// @see io.netty.bootstrap.AbstractBootstrap.option
setChannelOptions(channel, newOptionsArray(), logger);

// 为 NioServerSocketChannel 配置自定义属性
// newAttributesArray 方法返回的就是由 serverBootstrap.attr 方法添加的自定义属性
// @see io.netty.bootstrap.AbstractBootstrap.attr
setAttributes(channel, newAttributesArray());

ChannelPipeline p = channel.pipeline();

// 以下四个参数用于初始化 childGroup 中的 child,即:用于处理每一个已建立连接发生的I/O读写事件

// 获取 childGroup,即: 用于处理每一个已建立连接发生的I/O读写事件
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions = newOptionsArray(childOptions);
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = newAttributesArray(childAttrs);

p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
// 注意:这里的 ch 和上面的 channel 是同一个对象,即: NioServerSocketChannel

// 从 NioServerSocketChannel 中取出 pipeline
final ChannelPipeline pipeline = ch.pipeline();

// 为 NioServerSocketChannel 的 pipeline 添加 handler
// config.handler 方法返回的 handler 就是由 serverBootstrap.handler 方法配置的
// @see io.netty.bootstrap.AbstractBootstrap.handler
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}

// 向 NioServerSocketChannel 所属的 NioEventLoop 提交一个异步任务
ch.eventLoop().execute(new Runnable() {
public void run() {
// ServerBootstrapAcceptor 用于将建立连接的 SocketChannel 转发给 childGroup
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}

为 NioServerSocketChannel 装配 pipeline 流水线,需要注意以下信息:

  • line:25 -> NioServerSocketChannel 初始化时,会向 pipeline 中添加一个 ChannelInitializer 处理器。这是一个特殊的 入站处理器,它的 initChannel 方法在该 NioServerSocketChanne 注册到 NioEventLoop 的 Selector(多路复用器)[1] 后才会被触发调用

    确切的说是在 NioServerSocketChanne 注册完成后,触发 handlerAdded事件,该事件会触发 pipeline 中添加的 ChannelInitializer 的 handlerAdded 方法,详见: handlerAdded事件 小节。


  • line:34 -> ChannelInitializer 的 initChannel 方法中,会向 pipeline 添加自定义的 Handler,由 serverBootstrap.handler 方法配置,基于 示例代码(点击跳转),此处是 LoggingHandler。


  • line:40 -> 向 NioServerSocketChannel 所属的 NioEventLoop 提交一个 Runnable 异步任务。
    这个 Runnable 异步任务的作用就是在 NioServerSocketChannel 的 pipeline 中添加一个 ServerBootstrapAcceptor 处理器。
    ServerBootstrapAcceptor 是一个特殊的入站处理器,它的作用就是当建立新的 SocketChannel 连接时,将 SocketChannel 注册到 childGroup 中的某个 NioEventLoop 的 Selector(多路复用器)[1]


这里可能会有个问题,为什么不干脆直接调用 ChannelPipeline#addLast(io.netty.channel.ChannelHandler...) 方法,直接将 ChannelHandler 添加到 pipeline 中,而是又使用到了 ChannelInitializer 呢?

初始化 NioServerSocketChannel 中 pipeline 的动作,需要等到 NioServerSocketChannel 注册到 parentGroup 中某个 NioEventLoop 的 Selector(多路复用器)[1] 上以后才可以进行初始化,当前只是创建好 NioServerSocketChannel,并未注册完成。


2.2.1. 流程图

初始化 NioServerSocketChannel 流程如下图:

初始化NioServerSocketChannel(新标签页中打开可放大图片)

2.3. 注册Channel

以上是 NioServerSocketChannel 的创建及初始化,下面就是将 NioServerSocketChannel 注册到 parentGroup 中的某个 NioEventLoop 的 Selector(多路复用器)[1] 上。

1
2
3
4
5
6
7
8
9
10
11
12
// io.netty.bootstrap.AbstractBootstrap#initAndRegister
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {...}

// 将 NioServerSocketChannel 注册到 parentGroup 中某个 NioEventLoop 的 Selector(多路复用器) 上。
ChannelFuture regFuture = config().group().register(channel);
...
}
  • line: 10 -> config().group() 方法返回的是 parentGroup,实际调用的是 io.netty.channel.MultithreadEventLoopGroup#register 方法。

2.3.1. 选择EventLoop

接上,MultithreadEventLoopGroup#register 相关源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
public ChannelFuture register(Channel channel) {
return next().register(channel);
}

// io.netty.channel.MultithreadEventLoopGroup#next
public EventLoop next() {
return (EventLoop) super.next();
}

// io.netty.util.concurrent.MultithreadEventExecutorGroup#next
public EventExecutor next() {
return chooser.next();
}

MultithreadEventLoopGroup#register 方法中会调用 next 方法,next 方法一路向上调用,最后调用到 chooser.next 方法。chooser 类似负载均衡器,用于从 parentGroup 中选取一个 NioEventLoop,然后将 NioServerSocketChannel 注册到其 Selector(多路复用器)[1] 上。

关于chooser,在 Netty_NioEventLoop创建 的「创建chooser」一节中有简略分析,此处不再赘述。

而 next 方法最终返回的是 NioEventLoop,因此这里实际调用的是 SingleThreadEventLoop#register 方法。


2.3.2. 注册前的准备

通过上面 next 方法,使用 chooser 从 parentGroup 中选取出一个 NioEventLoop 后,然后调用 NioEventLoop 的 register 方法。

在向上逐层调用 NioEventLoop 父类的 register 方法过程中,将 NioServerSocketChannel 和 NioEventLoop 包装成了 DefaultChannelPromise。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
public ChannelFuture register(Channel channel) {
return next().register(channel);
}

// io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.Channel)
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}

// io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.ChannelPromise)
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
  • line:8 -> DefaultChannelPromise 中的 channel 属性是 NioServerSocketChannel,executor 属性是 NioEventLoop。
  • line:14 -> 调用 AbstractChannel.AbstractUnsafe#register 方法,将 NioServerSocketChannel 注册到 NioEventLoop 的 Selector(多路复用器)[1] 上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// io.netty.channel.AbstractChannel.AbstractUnsafe#register
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
...
// 判断当前线程是否是 NioEventLoop 中的线程
if (eventLoop.inEventLoop()) {
// 注册
register0(promise);
} else {
try {
// 对于 NioEventLoop 启动内部工作线程来说,重点方法是: eventLoop.execute(...)
eventLoop.execute(new Runnable() {
public void run() {
// 对于 NioServerSocketChannel 来说,重点关注注册过程。
register0(promise);
}
});
} catch (Throwable t) {...}
}
}

上面这段代码,在 Netty_NioEventLoop启动和运行 的「NioEventLoop启动」一节中曾经分析过: NioEventLoop 创建后,内部工作线程并不会立即运行,NioEventLoop 创建内部工作线程的时机是在第一个任务提交时

当时重点关注的是 line:11 -> eventLoop.execute(...) 方法,而注册逻辑(将 NioServerSocketChannel 注册到 NioEventLoop 的 Selector[1] 上)被包装成 Runnable 放入队列待运行,下面就需要着重关注 register0 方法逻辑。


2.3.3. 注册到EventLoop

register0 方法在 AbstractChannel.AbstractUnsafe 内部抽象类中,可以预见接下来会调用一堆子类实现的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// io.netty.channel.AbstractChannel.AbstractUnsafe#register0

private void register0(ChannelPromise promise) {
try {
// 检查注册操作是否已经取消,或者对应 channel 是否已经关闭
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
...
// 调用 JDK 层面的 register() 方法进行注册
doRegister();
neverRegistered = false;
registered = true;

// 触发 handlerAdded 事件
// 回调 pipeline 中添加的 ChannelInitializer 的 handlerAdded 方法,在这里初始化 channelPipeline。
pipeline.invokeHandlerAddedIfNeeded();

// 设置 regFuture 为 success,
// 触发 operationComplete 回调,将 bind 操作放入 Reactor 的任务队列中,等待 Reactor 线程执行。
safeSetSuccess(promise);

// 触发 channelRegistered 事件
pipeline.fireChannelRegistered();

// 对于服务端 ServerSocketChannel 来说 只有绑定端口地址成功后 channel 的状态才是 active 的,
// 此时绑定操作作为异步任务在 Reactor 的任务队列中,绑定操作还没开始,所以这里的 isActive() 是 false,
// 注册时不活跃,绑定端口后活跃
if (isActive()) {
if (firstRegistration) {
// 触发 channelActive 事件
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {...}
}
  1. line: 6 -> !promise.setUncancellable() 逻辑是检查 NioServerSocketChannel 的注册动作是否被取消了。!ensureOpen(promise) 逻辑是检查要注册的 NioServerSocketChannel 是否已经被关闭。如果 NioServerSocketChannel 已经关闭或者注册操作已经被取消,那么就直接返回,停止后续注册流程。

  2. line: 11 -> 执行真正的注册操作,最终实现在 AbstractChannel 的子类 AbstractNioChannel 中,调用 JDK 层面的 API 将 NioServerSocketChannel 注册到 NioEventLoop 的 Selector(多路复用器)[1] 上。

  3. line: 17 -> 触发 HandlerAdded 事件,回调 pipeline 中添加的 ChannelInitializer 的 handlerAdded 方法,在这里初始化 channelPipeline。

  4. line: 21 -> 设置 regFuture 为 Success,并回调注册在 regFuture 上的 ChannelFutureListener#operationComplete 方法,在 operationComplete 回调方法中将绑定操作封装成异步任务,提交到 taskQueue中,等待执行。

  5. line: 24 -> 触发 channelRegistered 事件。

  6. line: 32 -> Channel 状态为活跃时,触发 channelActive 事件。


2.3.3.1. doRegister

在 doRegister 方法中调用 JDK 底层 NIO API java.nio.channels.SelectableChannel#register,将 NioServerSocketChannel 中包装的 JDK NIO 原生 ServerSocketChannel 注册到 Selector(多路复用器)[1] 上。

1
2
3
4
5
6
7
8
9
10
11
12
// io.netty.channel.nio.AbstractNioChannel#doRegister

protected void doRegister() throws Exception {
// 表示注册操作是否成功
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {...}
}
}
  • line: 8 -> 当前 Channel 是 NioServerSocketChannel,因此 javaChannel() 方法返回的是 java.nio.channels.ServerSocketChannel,详阅:Netty_NioServerSocketChannel与NioSocketChannel 中「NioServerSocketChannel构造函数」一节。

  • line: 8 -> 所谓的 unwrappedSelector 是指被Netty优化过的 JDK NIO 原生 Selector [1],详阅:Netty_NioEventLoop创建 中「优化NIO原生Selector」一节。

  • line: 8 -> SelectableChannel#register 方法参数的含义:

    • Selector - 表示 JDK NIO Channel 将要向哪个 Selector[1] 进行注册,即:将 java.nio.channels.ServerSocketChannel 注册到 unwrappedSelector 上。

    • ops -> 表示 Channel 感兴趣的 IO事件,当对应的 IO事件就绪 时, Selector 会返回 Channel 对应的 SelectionKey 。

    • attachment -> 向 SelectionKey 中添加用户自定义的附加对象。
      此处传进去的 this 是 Netty 自己的 Channel,也就是 NioServerSocketChannel,这步操作非常巧妙。之前可能会疑惑,Selector 每次返回的是 jdk 底层的 Channel,那么 Netty 是怎么知道它对应哪个 Netty Channel 的呢?
      这里找到了答案:通过 attachment 属性,完成 Netty 自定义 Channel(也就是 NioServerSocketChannel) 与JDK NIO Channel 的关系绑定。这样每次对 Selector(多路复用器) 进行 I/O 就绪事件轮询 时,Netty 都可以从 Selector 返回的 SelectionKey 中获取到自定义的 Channel 对象(NioServerSocketChannel)。


javaChannel().register() 方法需要指定监听的网络操作位来表示 Channel 对哪几类网络事件感兴趣,定义如下:

  • 读 - public static final int OP_READ = 1 << 0;
  • 写 - public static final int OP_WRITE = 1 << 2;
  • 客户端连接 - public static final int OP_CONNECT = 1 << 3;
  • 服务端连接 - public static final int OP_ACCEPT = 1 << 4;

如上源码示例中,op传值是0,代表对任何事件都不感兴趣,仅为了完成注册操作。


完成 NioServerSocketChannel 注册后,会触发 NioServerSocketChannel 上 ChannelPipeline 的 handlerAddedchannelRegistered 两个事件。


2.3.3.2. handlerAdded事件

pipeline.invokeHandlerAddedIfNeeded(); 方法触发 handlerAdded 事件,然后回调 pipeline 中添加的 ChannelInitializer 的 handlerAdded 方法,即:io.netty.channel.ChannelInitializer#handlerAdded

本篇中有两处 ChannelInitializer ,一处是源码内部 初始化 NioServerSocketChannel(点击跳转),另一处是 示例代码(点击跳转) 中通过 serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)) 方法添加 Handler。

回顾 示例代码(点击跳转) ,通过 serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)) 方法添加 Handler,如果想添加多个处理器怎么办?只需要传入一个 ChannelInitializer,然后在 initChannel() 方法中添加处理器就行,就像下面这样:

1
2
3
4
5
6
7
8
9
10
11
serverBootstrap
.group(parentNioEventLoopGroup, childNioEventLoopGroup)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//..
ch.pipeline().addLast(handlerA);
ch.pipeline().addLast(handlerB);
//...
}
})

这是日常开发中使用比较多的写法,为什么在 ChannelInitializer 的 initChannel 方法中可以为 pipeline 添加 Handler?答案正是在 ChannelInitializer 的 handlerAdded 事件回调中。

ChannelInitializer 会先调用用户实现类的 initChannel 方法添加用户的 Handler,然后把自己(即:ChannelInitializer)从 pipeline 移出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// io.netty.channel.ChannelInitializer

public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
if (initChannel(ctx)) {
// 初始化工作完成后,需要将自身(ChannelInitializer)从 pipeline 中移除
removeState(ctx);
}
}
}

private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) {
try {
// 调用用户实现的 ChannelInitializer#initChannel() 方法
initChannel((C) ctx.channel());
} catch (Throwable cause) { ... } finally {
if (!ctx.isRemoved()) { ctx.pipeline().remove(this); }
}
return true;
}
return false;
}
  • line: 4 -> ctx.channel().isRegistered() 逻辑判断当前 Channel(NioServerSocketChannel) 是否已经完成注册,只有 Channel 注册完成后才可以进行 pipeline 的初始化。

  • line: 5 -> 调用 initChannel 方法,准备执行初始化 pipeline 逻辑。

  • line: 16 -> 调用 ChannelInitializer 匿名类的 initChannel 方法执行自定义的初始化逻辑。

  • line: 7 -> pipeline.remove(this) 逻辑,当执行完 line: 5 -> initChannel 方法后,pipeline 的初始化就结束了,此时 ChannelInitializer 就没必要再继续呆在 pipeline 中了,因此将 ChannelInitializer 从 pipeline 中删除。

初始化NioServerSocketChannel(点击跳转) 中就曾经向 NioServerSocketChannel 的 pipeline 中添加 ServerBootstrapAcceptor,结合到一起用图表示如下:

ChannelInitializer_initChannel(图片来源:https://huzb.me/)

2.3.3.3. safeSetSuccess

AbstractChannel.AbstractUnsafe#safeSetSuccess 方法中,回调注册在regFuture上的 ChannelFutureListener,在 ChannelFutureListener 的回调函数中开始发起绑端口地址流程


2.4. 流程图

注册 NioServerSocketChannel 的流程如下图:

initAndRegister流程图(新标签页中打开可放大图片)

3. doBind0

initAndRegister(点击跳转) 小节中分析了 创建、初始化 NioServerSocketChannel,并将 NioServerSocketChannel 异步注册到 parentGroup 中某个 NioEventLoop 的 Selector(多路复用器)[1] 上的过程

值得注意的是,以上整个过程中,注册的过程是异步的,所以在 initAndRegister 方法调用后返回一个代表注册结果的 regFuture ChannelFuture

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// io.netty.bootstrap.AbstractBootstrap#doBind

private ChannelFuture doBind(final SocketAddress localAddress) {

// 创建、初始化、注册 NioServerSocketChannel, 这是一个异步的过程
final ChannelFuture regFuture = initAndRegister();

// 此处的 channel 就是 NioServerSocketChannel, 由 serverBootstrap.channel 方法设置。
final Channel channel = regFuture.channel();
...
if (regFuture.isDone()) { // 注册完成并且成功
ChannelPromise promise = channel.newPromise();
// 绑定端口
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else { // 由于是异步,此时注册还没有完成,但总是会完成的
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
// 添加回调监听器
regFuture.addListener( (ChannelFutureListener) (futer) -> {
...
// 绑定端口
doBind0(regFuture, channel, localAddress, promise);
...
});
return promise;
}
}

之后会向 ChannelFuture regFuture 添加一个回调函数(ChannelFutureListener),在注册完成后执行。在回调函数中开始发起绑端口地址流程

在将 NioServerSocketChanne 注册到 parentGroup 的过程中,即: safeSetSuccess 小节中,回调注册在regFuture上的 ChannelFutureListener,在 ChannelFutureListener 的回调函数中开始发起绑端口地址流程

暂不深入研究

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// io.netty.channel.AbstractChannel.AbstractUnsafe#bind

public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
...
// 绑定前 isActive() 为 false
boolean wasActive = isActive();
try {
// 调用 JDK 底层绑定
doBind(localAddress);
} catch (Throwable t) { ... }

// 绑定后 isActive() 为 true
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
// 触发 ChannelActive 事件
public void run() { pipeline.fireChannelActive(); }
});
}
safeSetSuccess(promise);
}

4. 示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Slf4j
public class NettyEchoServer {
public static void main(String[] args) {
final AttributeKey<String> key = AttributeKey.valueOf(UUID.randomUUID().toString());
EventLoopGroup parentNioEventLoopGroup = new NioEventLoopGroup();
EventLoopGroup childNioEventLoopGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
.group(parentNioEventLoopGroup, childNioEventLoopGroup)
.handler(new LoggingHandler(LogLevel.INFO))
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.attr(key, "value")
.localAddress(SERVER_PORT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new NettyEchoServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind().sync();
log.info("Echo 服务端准备就绪...");
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {...} finally { ... }
}
}

如示例代码不合胃口,可以食用官方源码中的Test:io.netty.bootstrap.ServerBootstrapTest#testHandlerRegister


5. 流程图

Netty ServerBootstrap 启动完整流程图如下图:

ServerBootstrap启动过程粗略流程图(新标签页中打开可放大图片)

6. Reference



  1. 1.java.nio.channels.Selector(多路复用器) 可以看成是操作系统底层中 select/epoll/poll 多路复用函数的Java包装类。只要 Selector 监听的任一一个 Channel 上有事件发生,就可以通过 Selector.select() 方法监测到,并且使用 SelectedKeys 可以得到对应的 Channel,然后对它们执行相应的I/O操作。