register0() 方法详解
本文详细介绍 AbstractChannel 中的 register0() 方法,这是 Channel 注册到 EventLoop 的核心实现。
一、方法概述
1.1 方法签名
// AbstractChannel.AbstractUnsafe
private void register0(ChannelPromise promise) {
// 实现代码
}
1.2 调用时机
register0() 方法是在 register(EventLoop, ChannelPromise) 方法中被调用的:
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
// ... 参数校验 ...
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
// 如果当前线程就是 EventLoop 线程,直接调用
register0(promise);
} else {
// 否则,提交任务到 EventLoop 的任务队列
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
}
关键点:
register0()一定是在 EventLoop 线程中执行的- 要么直接执行(当前线程就是 EventLoop 线程)
- 要么通过
eventLoop.execute()提交任务后执行
1.3 方法作用
register0() 方法完成以下核心工作:
- 将 Channel 注册到 Selector 上(JDK 底层操作)
- 触发 pipeline 中 handler 的
handlerAdded()回调 - 触发
channelRegistered事件 - 如果 Channel 已激活,触发
channelActive事件
二、完整源码分析
2.1 完整代码
private void register0(ChannelPromise promise) {
try {
// 1. 检查 Channel 是否仍然打开
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
// 2. 标记是否是第一次注册
boolean firstRegistration = neverRegistered;
// 3. 执行实际的注册操作(JDK 底层)
doRegister();
// 4. 更新注册状态
neverRegistered = false;
registered = true;
// 5. 触发 handlerAdded 事件
pipeline.invokeHandlerAddedIfNeeded();
// 6. 设置 promise 为成功
safeSetSuccess(promise);
// 7. 触发 channelRegistered 事件
pipeline.fireChannelRegistered();
// 8. 如果 Channel 已激活,触发 channelActive 事件
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
// 9. 异常处理
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
2.2 执行流程图
register0() 开始
↓
检查 Channel 是否打开
↓
记录是否首次注册(firstRegistration)
↓
doRegister() - 将 Channel 注册到 Selector
↓
更新状态标记(neverRegistered = false, registered = true)
↓
pipeline.invokeHandlerAddedIfNeeded() - 触发 handlerAdded
↓
safeSetSuccess(promise) - 设置 promise 成功
↓
pipeline.fireChannelRegistered() - 触发 channelRegistered 事件
↓
判断 Channel 是否激活?
├─ 是 → 判断是否首次注册?
│ ├─ 是 → fireChannelActive()
│ └─ 否 → 判断是否 autoRead?
│ └─ 是 → beginRead()
└─ 否 → 结束
三、逐步详解
步骤 1:检查 Channel 是否仍然打开
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
promise.setUncancellable()
// DefaultPromise
@Override
public boolean setUncancellable() {
if (RESULT_UPDATER.compareAndSet(this, null, UNCANCELLABLE)) {
return true;
}
Object result = this.result;
return !isDone0(result) || !isCancelled0(result);
}
作用:
- 将 promise 标记为不可取消状态
- 防止在注册过程中被取消
- 使用 CAS 操作保证线程安全
返回值:
true:设置成功,或者 promise 已经完成但不是被取消的false:promise 已经被取消
ensureOpen(promise)
protected final boolean ensureOpen(ChannelPromise promise) {
if (isOpen()) {
return true;
}
safeSetFailure(promise, newClosedChannelException(initialCloseCause));
return false;
}
作用:
- 检查 Channel 是否仍然打开
- 如果已关闭,设置 promise 失败并返回 false
为什么需要这个检查?
- register 操作可能是异步的(通过 eventLoop.execute 提交)
- 在任务排队等待执行期间,Channel 可能已经被关闭
- 需要在真正执行注册前再次确认 Channel 状态
步骤 2:标记是否是第一次注册
boolean firstRegistration = neverRegistered;
neverRegistered 字段:
// AbstractChannel
private volatile boolean neverRegistered = true;
作用:
- 记录这个 Channel 是否从未注册过
- 用于后续判断是否需要触发
channelActive事件 - Netty 支持 Channel 的 deregister 和 re-register,所以需要这个标记
使用场景:
- 第一次注册:
neverRegistered = true,需要触发完整的初始化流程 - 重新注册:
neverRegistered = false,只需要重新开始读取数据
步骤 3:执行实际的注册操作(doRegister)
doRegister();
这是最核心的一步,将 Channel 注册到 Selector 上。
NioSocketChannel 的 doRegister 实现
// AbstractNioChannel
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 调用 JDK 的 Channel.register() 方法
// 参数说明:
// - eventLoop().unwrappedSelector():获取 Selector
// - 0:初始不监听任何事件
// - this:将当前 NioSocketChannel 作为 attachment 附加到 SelectionKey 上
selectionKey = javaChannel().register(
eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
// 如果注册失败,强制 selectNow() 清理已取消的 key
eventLoop().selectNow();
selected = true;
} else {
// 如果再次失败,抛出异常
throw e;
}
}
}
}
关键点详解:
1. javaChannel() 是什么?
// AbstractNioChannel
protected SelectableChannel javaChannel() {
return ch;
}
// 在 NioSocketChannel 构造时创建
public NioSocketChannel() {
this(DEFAULT_SELECTOR_PROVIDER);
}
public NioSocketChannel(SelectorProvider provider) {
this(newSocket(provider)); // 创建 JDK 的 SocketChannel
}
private static SocketChannel newSocket(SelectorProvider provider) {
return provider.openSocketChannel();
}
javaChannel()返回的是 JDK NIO 的SocketChannel实例- 这是在 NioSocketChannel 构造时创建的
- 对于服务端,返回的是
ServerSocketChannel
2. 为什么 interestOps 设置为 0?
selectionKey = javaChannel().register(selector, 0, this);
原因:
- 此时只是完成注册,还不需要监听任何事件
- 后续会在合适的时机修改 interestOps
- 对于客户端:在 connect 完成后,会设置为
OP_READ - 对于服务端:在 bind 完成后,会设置为
OP_ACCEPT
何时修改 interestOps?
// 在 channelActive 事件中
pipeline.fireChannelActive()
↓
HeadContext.channelActive()
↓
readIfIsAutoRead()
↓
channel.read()
↓
AbstractChannel.read()
↓
pipeline.read()
↓
HeadContext.read()
↓
unsafe.beginRead()
↓
AbstractNioChannel.doBeginRead()
↓
selectionKey.interestOps(interestOps | readInterestOp);
3. 为什么将 this 作为 attachment?
selectionKey = javaChannel().register(selector, 0, this);
作用:
- 将 Netty 的 Channel(NioSocketChannel)附加到 SelectionKey 上
- 当 Selector 检测到事件时,可以通过
key.attachment()获取到对应的 Channel - 这样就建立了 JDK Channel 和 Netty Channel 的关联
使用场景:
// 在 NioEventLoop 处理 IO 事件时
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
// ch 就是从 attachment 中获取的
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
int readyOps = k.readyOps();
if ((readyOps & SelectionKey.OP_READ) != 0) {
unsafe.read(); // 处理读事件
}
}
4. 为什么要循环重试?
for (;;) {
try {
selectionKey = javaChannel().register(selector, 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
原因:
- 如果 SelectionKey 已经被取消,register 会抛出
CancelledKeyException - 调用
selectNow()可以清理已取消的 key - 然后重试注册
- 如果重试还失败,说明有其他问题,抛出异常
步骤 4:更新注册状态
neverRegistered = false;
registered = true;
neverRegistered
private volatile boolean neverRegistered = true;
- 初始值为
true,表示从未注册过 - 注册成功后设置为
false - 即使后续 deregister,这个值也不会再变回
true - 用于区分"第一次注册"和"重新注册"
registered
private volatile boolean registered;
- 表示当前是否已注册
- 注册成功后设置为
true - deregister 后会设置为
false - 用于判断当前 Channel 的注册状态
两者的区别:
第一次注册:
neverRegistered: true -> false
registered: false -> true
deregister:
neverRegistered: false(不变)
registered: true -> false
重新注册:
neverRegistered: false(不变)
registered: false -> true
步骤 5:触发 handlerAdded 事件
pipeline.invokeHandlerAddedIfNeeded();
这是一个非常重要的步骤,会触发 pipeline 中所有 handler 的 handlerAdded() 回调。
为什么需要这个方法?
在 Channel 创建时,我们会往 pipeline 中添加 handler:
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new LoggingHandler());
ch.pipeline().addLast(new MyHandler());
}
});
但是,ChannelInitializer 的 initChannel() 方法什么时候被调用呢?就是在这里!
调用链路
pipeline.invokeHandlerAddedIfNeeded()
↓
DefaultChannelPipeline.invokeHandlerAddedIfNeeded()
↓
callHandlerAddedForAllHandlers()
↓
遍历 pipeline 中的所有 handler
↓
调用每个 handler 的 handlerAdded() 方法
↓
ChannelInitializer.handlerAdded()
↓
initChannel(channel) // 这里会执行我们自定义的初始化逻辑
↓
将 ChannelInitializer 从 pipeline 中移除
ChannelInitializer 的实现
public abstract class ChannelInitializer<C extends Channel>
extends ChannelInboundHandlerAdapter {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// 如果 Channel 已注册,立即执行初始化
if (initChannel(ctx)) {
// 初始化完成后,移除自己
removeState(ctx);
}
}
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) {
try {
// 调用用户实现的 initChannel 方法
initChannel((C) ctx.channel());
} catch (Throwable cause) {
exceptionCaught(ctx, cause);
} finally {
// 从 pipeline 中移除 ChannelInitializer
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
// 用户需要实现这个方法
protected abstract void initChannel(C ch) throws Exception;
}
执行效果:
注册前的 pipeline:
head -> ChannelInitializer -> tail
执行 invokeHandlerAddedIfNeeded() 后:
head -> LoggingHandler -> MyHandler -> tail
ChannelInitializer 已被移除
步骤 6:设置 promise 为成功
safeSetSuccess(promise);
safeSetSuccess 的实现
protected final void safeSetSuccess(ChannelPromise promise) {
if (!(promise instanceof VoidChannelPromise) && !promise.trySuccess()) {
logger.warn("Failed to mark a promise as success because it is done already: {}", promise);
}
}
作用:
- 将 promise 标记为成功状态
- 通知等待这个 promise 的线程
- 触发 promise 上注册的 listener
为什么叫 “safe”?
- 使用
trySuccess()而不是setSuccess() trySuccess()如果 promise 已经完成,会返回 false,不会抛异常setSuccess()如果 promise 已经完成,会抛出IllegalStateException
promise 的作用
// 在调用 register 时创建的 promise
ChannelFuture regFuture = config().group().register(channel);
// 可以同步等待注册完成
regFuture.sync();
// 或者添加 listener 异步处理
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
System.out.println("注册成功");
} else {
System.out.println("注册失败: " + future.cause());
}
}
});
设置成功后的效果:
regFuture.isSuccess()返回trueregFuture.sync()会立即返回(如果在等待)- 所有注册的 listener 会被调用
步骤 7:触发 channelRegistered 事件
pipeline.fireChannelRegistered();
这会触发 pipeline 中所有 Inbound handler 的 channelRegistered() 方法。
事件传播流程
pipeline.fireChannelRegistered()
↓
从 head 开始传播
↓
HeadContext.channelRegistered(ctx)
↓
ctx.fireChannelRegistered() // 传播给下一个 handler
↓
LoggingHandler.channelRegistered(ctx)
↓
ctx.fireChannelRegistered() // 继续传播
↓
MyHandler.channelRegistered(ctx)
↓
ctx.fireChannelRegistered() // 继续传播
↓
TailContext.channelRegistered(ctx)
用户可以在 handler 中处理这个事件
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("Channel 已注册到 EventLoop");
// 继续传播事件
ctx.fireChannelRegistered();
}
}
注意:
- 这个事件表示 Channel 已经注册到 EventLoop
- 但此时 Channel 可能还没有激活(对于客户端,还没有 connect;对于服务端,还没有 bind)
- 所以通常不在这个事件中做太多事情
步骤 8:如果 Channel 已激活,触发 channelActive 事件
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
这是一个条件判断,根据不同情况执行不同的逻辑。
isActive() 的判断
// NioSocketChannel
@Override
public boolean isActive() {
SocketChannel ch = javaChannel();
return ch.isOpen() && ch.isConnected();
}
// NioServerSocketChannel
@Override
public boolean isActive() {
return javaChannel().socket().isBound();
}
对于客户端(NioSocketChannel):
- 需要 Channel 打开 且 已连接
- 在 register 时,通常还没有 connect,所以
isActive()返回false - 在 connect 完成后,会再次调用
pipeline.fireChannelActive()
对于服务端(NioServerSocketChannel):
- 需要 Channel 已绑定端口
- 在 register 时,如果已经 bind,
isActive()返回true - 会触发
channelActive事件
第一次注册的处理
if (firstRegistration) {
pipeline.fireChannelActive();
}
触发 channelActive 事件的时机:
- 服务端 NioServerSocketChannel:
bind(port)
↓
doBind()
↓
initAndRegister()
↓
register0()
↓
此时 isActive() = true(已 bind)
firstRegistration = true
↓
触发 fireChannelActive()
- 客户端 NioSocketChannel:
connect(host, port)
↓
doConnect()
↓
initAndRegister()
↓
register0()
↓
此时 isActive() = false(还未 connect)
不触发 fireChannelActive()
↓
connect 完成后
↓
finishConnect()
↓
触发 fireChannelActive()
channelActive 事件的重要性
// HeadContext.channelActive()
@Override
public void channelActive(ChannelHandlerContext ctx) {
// 1. 传播事件
ctx.fireChannelActive();
// 2. 如果设置了 autoRead,开始读取数据
readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
channel.read() 会做什么?
// AbstractChannel
@Override
public Channel read() {
pipeline.read();
return this;
}
// HeadContext.read()
@Override
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
// AbstractNioChannel.doBeginRead()
@Override
protected void doBeginRead() throws Exception {
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
// 设置感兴趣的事件
// 对于客户端:OP_READ
// 对于服务端:OP_ACCEPT
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
关键点:
- 在
channelActive事件中,会设置 SelectionKey 的 interestOps - 对于客户端:设置为
OP_READ,开始监听读事件 - 对于服务端:设置为
OP_ACCEPT,开始监听连接事件 - 这就是为什么在
doRegister()时 interestOps 设置为 0 的原因
重新注册的处理
else if (config().isAutoRead()) {
beginRead();
}
场景:
- Channel 之前已经注册过(
firstRegistration = false) - 然后被 deregister
- 现在重新 register
处理:
- 如果设置了 autoRead,直接调用
beginRead() - 重新开始监听 IO 事件
- 不需要再触发
channelActive事件(因为不是第一次激活)
步骤 9:异常处理
catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
如果注册过程中发生任何异常,需要进行清理工作。
closeForcibly()
// AbstractChannel.AbstractUnsafe
@Override
public final void closeForcibly() {
assertEventLoop();
try {
doClose();
} catch (Exception e) {
logger.warn("Failed to close a channel.", e);
}
}
作用:
- 强制关闭 Channel
- 不触发任何事件
- 直接调用底层的 close 操作
- 避免文件描述符泄漏
closeFuture.setClosed()
closeFuture.setClosed();
作用:
- 设置 closeFuture 为已关闭状态
- 通知等待 Channel 关闭的线程
safeSetFailure(promise, t)
protected final void safeSetFailure(ChannelPromise promise, Throwable cause) {
if (!(promise instanceof VoidChannelPromise) && !promise.tryFailure(cause)) {
logger.warn("Failed to mark a promise as failure because it's done already: {}", promise, cause);
}
}
作用:
- 将 promise 标记为失败状态
- 设置失败原因
- 通知等待的线程和 listener
效果:
ChannelFuture regFuture = bootstrap.register();
// 如果注册失败
regFuture.isSuccess(); // 返回 false
regFuture.cause(); // 返回异常对象
// 或者在 listener 中处理
regFuture.addListener(future -> {
if (!future.isSuccess()) {
System.err.println("注册失败: " + future.cause());
}
});
四、完整的注册流程
4.1 客户端的注册流程
Bootstrap.connect(host, port)
↓
doResolveAndConnect()
↓
initAndRegister()
↓
channelFactory.newChannel() // 创建 NioSocketChannel
↓
init(channel) // 初始化 channel,添加 ChannelInitializer
↓
config().group().register(channel) // 注册到 EventLoopGroup
↓
next().register(channel) // 选择一个 EventLoop
↓
register(new DefaultChannelPromise(channel, this))
↓
promise.channel().unsafe().register(this, promise)
↓
AbstractChannel.this.eventLoop = eventLoop // 绑定 EventLoop
↓
eventLoop.execute(() -> register0(promise)) // 提交任务
↓
【EventLoop 线程执行】
↓
register0(promise)
↓
doRegister() // 注册到 Selector,interestOps = 0
↓
pipeline.invokeHandlerAddedIfNeeded() // 触发 handlerAdded
↓
ChannelInitializer.initChannel() // 执行用户的初始化逻辑
↓
safeSetSuccess(promise) // 设置 promise 成功
↓
pipeline.fireChannelRegistered() // 触发 channelRegistered 事件
↓
isActive() = false(还未 connect)
↓
注册完成,返回
↓
【回到主线程】
↓
doResolveAndConnect0()
↓
doConnect() // 执行连接操作
↓
connect 完成后
↓
finishConnect()
↓
pipeline.fireChannelActive() // 触发 channelActive 事件
↓
设置 interestOps = OP_READ // 开始监听读事件
4.2 服务端的注册流程
ServerBootstrap.bind(port)
↓
doBind()
↓
initAndRegister()
↓
channelFactory.newChannel() // 创建 NioServerSocketChannel
↓
init(channel) // 初始化 channel
↓
config().group().register(channel) // 注册到 bossGroup
↓
【与客户端类似的注册流程】
↓
register0(promise)
↓
doRegister() // 注册到 Selector
↓
pipeline.invokeHandlerAddedIfNeeded()
↓
safeSetSuccess(promise)
↓
pipeline.fireChannelRegistered()
↓
isActive() = false(还未 bind)
↓
注册完成,返回
↓
【回到主线程】
↓
doBind0()
↓
channel.bind(localAddress, promise)
↓
doBind() // 执行绑定操作
↓
bind 完成后
↓
pipeline.fireChannelActive() // 触发 channelActive 事件
↓
设置 interestOps = OP_ACCEPT // 开始监听连接事件
五、关键时序图
5.1 register0 方法的执行时序
调用线程 EventLoop 线程 Selector
| | |
| register(eventLoop) | |
|----------------------->| |
| | |
| eventLoop.execute() | |
|----------------------->| |
| | |
| 返回 ChannelFuture | |
|<-----------------------| |
| | |
| | 从 taskQueue 取任务 |
| | |
| | register0() |
| | |
| | doRegister() |
| |----------------------->|
| | | javaChannel.register()
| |<-----------------------|
| | |
| | invokeHandlerAdded() |
| | -> ChannelInitializer |
| | |
| | safeSetSuccess() |
| | |
| promise 完成 | |
|<-----------------------| |
| | |
| listener 回调 | |
|<-----------------------| |
| | |
| | fireChannelRegistered()|
| | |
| | fireChannelActive() |
| | (如果已激活) |
| | |
5.2 ChannelInitializer 的执行时序
register0()
↓
pipeline.invokeHandlerAddedIfNeeded()
↓
遍历 pipeline 中的 handler
↓
找到 ChannelInitializer
↓
ChannelInitializer.handlerAdded(ctx)
↓
initChannel(ctx)
↓
执行用户的 initChannel(channel) 方法
↓
用户代码:
pipeline.addLast(new LoggingHandler());
pipeline.addLast(new MyHandler());
↓
从 pipeline 中移除 ChannelInitializer
↓
pipeline 结构变化:
之前:head -> ChannelInitializer -> tail
之后:head -> LoggingHandler -> MyHandler -> tail
六、重要概念辨析
6.1 register 和 active 的区别
register(注册)
- 表示 Channel 已经注册到 EventLoop 的 Selector 上
- 此时 Channel 可能还没有激活
- 触发
channelRegistered事件
active(激活)
- 对于客户端:表示连接已建立(connected)
- 对于服务端:表示端口已绑定(bound)
- 触发
channelActive事件
状态转换
客户端:
创建 -> 注册 -> 连接 -> 激活
↓ ↓ ↓
registered connected active
服务端:
创建 -> 注册 -> 绑定 -> 激活
↓ ↓ ↓
registered bound active
6.2 firstRegistration 的作用
boolean firstRegistration = neverRegistered;
为什么需要这个标记?
Netty 支持 Channel 的 deregister 和 re-register:
// 取消注册
channel.deregister().sync();
// 重新注册到另一个 EventLoop
anotherEventLoop.register(channel).sync();
不同场景的处理
第一次注册(firstRegistration = true):
- 需要触发完整的初始化流程
- 触发
channelActive事件 - 设置 interestOps
重新注册(firstRegistration = false):
- 不触发
channelActive事件(因为已经激活过了) - 如果设置了 autoRead,直接调用
beginRead() - 重新开始监听 IO 事件
6.3 interestOps 的设置时机
为什么 doRegister 时设置为 0?
selectionKey = javaChannel().register(selector, 0, this);
原因:
- 此时只是完成注册,还不需要监听事件
- 避免在初始化完成前就收到事件
- 等待 pipeline 初始化完成
什么时候设置真正的 interestOps?
pipeline.fireChannelActive()
↓
HeadContext.channelActive()
↓
readIfIsAutoRead()
↓
channel.read()
↓
unsafe.beginRead()
↓
doBeginRead()
↓
selectionKey.interestOps(interestOps | readInterestOp);
时机:
- 在
channelActive事件中 - 此时 pipeline 已经初始化完成
- 可以安全地开始监听 IO 事件
设置的值:
- 客户端 NioSocketChannel:
OP_READ - 服务端 NioServerSocketChannel:
OP_ACCEPT
七、常见问题
Q1: register0 为什么一定在 EventLoop 线程中执行?
答案:
- 线程安全:避免多线程并发修改 Channel 状态
- 顺序性:保证注册操作和后续操作的顺序
- 一致性:Channel 的所有操作都在同一个线程中,简化编程模型
Q2: 为什么需要 promise?
答案:
- 异步通知:register 是异步操作,通过 promise 通知调用者结果
- 错误处理:可以通过 promise 获取失败原因
- 回调机制:可以添加 listener 进行异步回调
ChannelFuture future = bootstrap.register();
// 方式1:同步等待
future.sync();
// 方式2:异步回调
future.addListener(f -> {
if (f.isSuccess()) {
System.out.println("注册成功");
} else {
System.err.println("注册失败: " + f.cause());
}
});
Q3: ChannelInitializer 什么时候被移除?
答案:
- 在
invokeHandlerAddedIfNeeded()中被移除 - 具体时机是在
ChannelInitializer.handlerAdded()方法中 - 执行完
initChannel()后,立即从 pipeline 中移除自己
为什么要移除?
- ChannelInitializer 只是一个辅助类,用于初始化 pipeline
- 初始化完成后就没有用了,应该移除
- 避免占用内存和影响性能
Q4: 如果 register 失败会怎样?
答案:
- 调用
closeForcibly()强制关闭 Channel - 设置
closeFuture为已关闭 - 设置
promise为失败状态 - 调用者可以通过 promise 获取失败原因
ChannelFuture future = bootstrap.register();
if (!future.isSuccess()) {
Throwable cause = future.cause();
// 处理失败情况
}
Q5: register 和 bind/connect 的关系?
答案:
对于服务端:
register -> bind -> channelActive
- 先注册到 EventLoop
- 再绑定端口
- 绑定成功后触发 channelActive
对于客户端:
register -> connect -> channelActive
- 先注册到 EventLoop
- 再连接服务器
- 连接成功后触发 channelActive
关键点:
- register 必须在 bind/connect 之前
- 因为 bind/connect 操作需要在 EventLoop 中执行
- 而 EventLoop 是在 register 时绑定的
Q6: 为什么 doRegister 要循环重试?
答案:
for (;;) {
try {
selectionKey = javaChannel().register(selector, 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
原因:
- 如果 SelectionKey 已经被取消,register 会抛出
CancelledKeyException - 调用
selectNow()可以清理已取消的 key - 然后重试注册
- 如果重试还失败,说明有其他问题,抛出异常
Q7: autoRead 是什么?
答案:
autoRead是 Channel 的一个配置选项- 默认值为
true - 表示在 Channel 激活后,自动开始读取数据
设置方式:
bootstrap.option(ChannelOption.AUTO_READ, true);
作用:
- 如果为
true,在channelActive时自动调用channel.read() - 如果为
false,需要手动调用channel.read()才会开始读取
使用场景:
- 通常保持默认值
true - 如果需要流量控制,可以设置为
false,手动控制读取时机
八、总结
8.1 register0 的核心职责
- 注册到 Selector:调用 JDK 的
channel.register()方法 - 初始化 pipeline:触发
handlerAdded事件,执行 ChannelInitializer - 通知调用者:设置 promise 为成功或失败
- 触发事件:触发
channelRegistered和channelActive事件 - 开始监听:设置 interestOps,开始监听 IO 事件
8.2 关键步骤回顾
1. 检查 Channel 是否打开
↓
2. 记录是否首次注册
↓
3. doRegister() - 注册到 Selector(interestOps = 0)
↓
4. 更新状态标记
↓
5. invokeHandlerAddedIfNeeded() - 初始化 pipeline
↓
6. safeSetSuccess(promise) - 通知调用者
↓
7. fireChannelRegistered() - 触发注册事件
↓
8. 如果已激活,触发 channelActive 事件
↓
9. 在 channelActive 中设置 interestOps
8.3 重要概念
- register:Channel 注册到 EventLoop 的 Selector
- active:Channel 已激活(客户端已连接,服务端已绑定)
- firstRegistration:区分首次注册和重新注册
- interestOps:先设置为 0,在 channelActive 时设置真正的值
- ChannelInitializer:在 handlerAdded 时执行初始化,然后移除
8.4 线程模型
调用线程(如 main 线程)
↓
调用 register(eventLoop, promise)
↓
绑定 EventLoop
↓
提交任务到 EventLoop 的 taskQueue
↓
返回 ChannelFuture
↓
【切换到 EventLoop 线程】
↓
从 taskQueue 取出任务
↓
执行 register0(promise)
↓
完成注册
↓
设置 promise 成功
↓
【回到调用线程】
↓
promise 完成,触发 listener
8.5 与其他方法的关系
initAndRegister()
↓
创建 Channel
↓
初始化 Channel(添加 ChannelInitializer)
↓
register(channel)
↓
选择 EventLoop
↓
register(eventLoop, promise)
↓
绑定 EventLoop
↓
提交 register0 任务
↓
【EventLoop 线程】
↓
register0(promise)
↓
doRegister() - JDK 注册
↓
invokeHandlerAddedIfNeeded() - 初始化 pipeline
↓
fireChannelRegistered() - 触发事件
↓
fireChannelActive() - 触发激活事件(如果已激活)
↓
设置 interestOps - 开始监听 IO 事件
附录:相关源码位置
核心类
io.netty.channel.AbstractChannel.AbstractUnsafeio.netty.channel.nio.AbstractNioChannelio.netty.channel.DefaultChannelPipelineio.netty.channel.ChannelInitializer
关键方法
AbstractChannel.AbstractUnsafe.register(EventLoop, ChannelPromise)AbstractChannel.AbstractUnsafe.register0(ChannelPromise)AbstractNioChannel.doRegister()DefaultChannelPipeline.invokeHandlerAddedIfNeeded()ChannelInitializer.handlerAdded(ChannelHandlerContext)
相关事件
channelRegistered:Channel 已注册到 EventLoopchannelActive:Channel 已激活(连接或绑定完成)handlerAdded:Handler 已添加到 pipeline
384



被折叠的 条评论
为什么被折叠?



