Netty源码分析---doRegister方法详解

Netty源码分析系列之服务端Channel注册 Netty中服务端channel是如何注册到多路复用器上的? 阅读详情

doRegister() 方法详解

方法概述

doRegister() 方法是 AbstractChannel 中 register0() 调用的核心方法,它负责将 Netty 的 Channel 与 JDK NIO 的底层 Channel 进行关联,并注册到 Selector 上。

源码分析

完整源码

@Override
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            // JDK NIO Channel 的 register 方法签名:
            // public final SelectionKey register(Selector sel, int ops, Object att)
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {
            if (!selected) {
                // 强制 Selector 立即执行 select 操作
                // 这会清理掉已取消的 SelectionKey
                eventLoop().selectNow();
                selected = true;
            } else {
                // 如果已经执行过 selectNow() 还是失败,说明有严重问题,抛出异常
                throw e;
            }
        }
    }
}

详细分析

1. 核心注册操作

selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);

这一行代码包含了几个关键点:

(1) javaChannel()
  • 返回 JDK NIO 的原生 Channel 对象
  • 对于 NioSocketChannel,返回的是 SocketChannel
  • 对于 NioServerSocketChannel,返回的是 ServerSocketChannel
(2) eventLoop().unwrappedSelector()
  • 获取当前 EventLoop 中的 Selector 实例
  • unwrappedSelector() 返回的是原始的 JDK Selector,而不是 Netty 优化后的包装版本
  • 这个 Selector 是在 NioEventLoop 构造时创建的
(3) 第二个参数:0

这是一个非常重要的设计!

  • 注册时将 interestOps 设置为 0,表示暂时不监听任何 IO 事件
  • 为什么不直接设置为 OP_READ 或 OP_ACCEPT?

原因:

  • 此时 pipeline 可能还没有完全构建好
  • handler 的 handlerAdded() 方法还没有被调用
  • 如果立即监听事件,可能会在 pipeline 准备好之前就收到 IO 事件
  • 真正的事件监听会在 channelActive 事件触发时通过 beginRead() 方法设置
(4) 第三个参数:this
  • 将 Netty 的 Channel 对象作为 attachment 附加到 SelectionKey 上
  • 这样在 Selector 检测到事件时,可以通过 SelectionKey.attachment() 快速获取到对应的 Netty Channel
  • 这是 Netty 将 JDK NIO 和自己的抽象层关联起来的关键

2. 返回值:SelectionKey

selectionKey = javaChannel().register(...);
  • 注册成功后,JDK 会返回一个 SelectionKey 对象
  • Netty 将这个 key 保存在 Channel 的成员变量中
  • 后续可以通过这个 key 来修改监听的事件类型(interestOps)

3. 异常处理:CancelledKeyException

catch (CancelledKeyException e) {
    if (!selected) {
        eventLoop().selectNow();
        selected = true;
    } else {
        throw e;
    }
}
为什么会抛出 CancelledKeyException?

当一个 SelectionKey 被取消后,如果再次尝试使用它,就会抛出这个异常。可能的场景:

  1. 之前的注册操作被取消了,但 key 还没有从 Selector 的 key set 中移除
  2. 在多线程环境下,另一个线程取消了这个 key
处理策略

第一次捕获异常时:

eventLoop().selectNow();
  • 调用 selectNow() 非阻塞地执行一次 select 操作
  • 这会触发 Selector 清理已取消的 SelectionKey
  • 清理完成后,重新尝试注册(因为在 for 循环中)

第二次还是失败:

  • 如果执行了 selectNow() 后重试还是失败,说明有更严重的问题
  • 直接抛出异常,让上层处理

4. 无限循环的设计

for (;;) {
    try {
        selectionKey = javaChannel().register(...);
        return;  // 成功后立即返回
    } catch (CancelledKeyException e) {
        // 处理异常,然后继续循环重试
    }
}
  • 使用无限循环是为了处理 CancelledKeyException 后能够重试
  • 正常情况下,第一次就会成功并 return
  • 只有在极少数情况下才会进入异常处理和重试逻辑

关键时序图

register0()
    |
    v
doRegister()
    |
    v
javaChannel().register(selector, 0, this)
    |
    v
返回 SelectionKey
    |
    v
保存到 selectionKey 成员变量
    |
    v
后续通过 selectionKey 修改 interestOps

与后续流程的关联

1. 注册后的状态

注册完成后:

  • Channel 已经和 Selector 关联
  • 但 interestOps = 0,不监听任何事件
  • SelectionKey 已保存,可以随时修改监听的事件

2. 何时开始真正监听事件?

在 register0() 方法的后续流程中:

if (isActive()) {
    if (firstRegistration) {
        pipeline.fireChannelActive();  // 触发 channelActive 事件
    }
}

channelActive 事件会触发 HeadContext 的处理:

// HeadContext.channelActive()
public void channelActive(ChannelHandlerContext ctx) {
    ctx.fireChannelActive();  // 继续传播事件
    readIfIsAutoRead();       // 如果配置了 autoRead,开始读取
}

private void readIfIsAutoRead() {
    if (channel.config().isAutoRead()) {
        channel.read();  // 最终会调用到 beginRead()
    }
}

beginRead() 方法会修改 interestOps:

protected void doBeginRead() throws Exception {
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }
    
    final int interestOps = selectionKey.interestOps();
    // readInterestOp 在构造时设置:
    // 客户端是 OP_READ,服务端是 OP_ACCEPT
    if ((interestOps & readInterestOp) == 0) {
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

设计精妙之处

1. 延迟监听策略

  • 注册时不监听:给 pipeline 构建留出时间
  • 激活时才监听:确保所有准备工作都已完成
  • 避免竞态条件:防止在未准备好时就收到 IO 事件

2. Attachment 机制

register(selector, 0, this)  // this 就是 Netty Channel

在 NioEventLoop 处理 IO 事件时:

// NioEventLoop.processSelectedKeys()
SelectionKey key = ...;
NioSocketChannel channel = (NioSocketChannel) key.attachment();
// 直接获取到 Netty Channel,无需额外查找

这种设计避免了维护一个 Map<SelectionKey, Channel> 的开销。

3. 异常恢复机制

通过 selectNow() + 重试的方式,优雅地处理了 SelectionKey 取消的边界情况,提高了系统的健壮性。

总结

doRegister() 方法虽然代码不多,但设计非常精妙:

  1. 桥接作用:连接 Netty Channel 和 JDK NIO Channel
  2. 延迟监听:注册时 interestOps=0,避免过早收到事件
  3. 关联机制:通过 attachment 建立双向关联
  4. 异常处理:优雅处理 SelectionKey 取消的情况
  5. 为后续铺路:保存 SelectionKey,供后续修改 interestOps 使用

这个方法是 Netty 启动流程中,从应用层到操作系统层的关键桥梁!

智能绿植管家:基于 ESP32 的室内植物自动补光与环境调控系统 本文设计的基于 ESP32 的室内植物自动补光与生长环境调控系统,实现了植物生长环境的自动化、智能化管理。硬件选型合理、接线简单,软件逻辑清晰、易于调试,能够有效解决室内植物生长环境不适的问题。系统具备良好的实用性和扩展性,可为园艺爱好者和小型培育场所提供稳定、高效的植物养护方案。 阅读详情

相关推荐

图像特征(一)——颜色特征(颜色直方图,颜色矩,颜色集,颜色聚合向量和颜色相关图)

本文主要是为了更好地理解图像特征,理解各方法的原理。转载请注明出处https://mp.youkuaiyun.com/console/editor/html/105090183 图像模式识别的一般步骤为:输入图像--图像预处理--特征提取--特征选择--图像分类--输出结果。 图像特征主要有:颜色特征、形状特征、纹理特征和空间关系特征。 本文主要介绍颜色特征,并给出matlab上实现的demo。 颜...

gloria_iris的博客 3万+

Netty源码分析---register0方法详解

摘要:本文详细解析了Netty中AbstractChannel.register0()方法的实现机制。该方法负责将Channel注册到EventLoop的核心流程,包括Selector注册、状态更新和事件触发。关键点包括:1) 确保在EventLoop线程执行;2) 通过doRegister()完成底层Selector注册;3) 触发handlerAdded和channelRegistered事件;4) 根据首次注册状态决定是否触发channelActive事件。该方法还包含完善的异常处理机制,是Netty

java_rutu的博客 531

简单的服务器取证

记一次入门服务器取证——药品直销网,涵盖旧版本宝塔、静态IP的配置更改、代码的理解

m0_74559567的博客 3756

网络编程与Netty(四) Netty源码-(Ⅲ)

Channel 系列源码分析 ​ 我们用到了两种Channel----NioServerSocketChannel和NioSocketChannel,先来看这两个类的关系图: NioServerSocketChannel NioSocketChannel ​ 看到这两个类的关系图可以发现,其实他们都有共同的父类(或实现的接口)Channel、AbstractChannel、AbstractNioChannel,而不同的是NioServerSocketChannel继承的是AbstractNioMessa

qq_38677263的博客 1058

AI嵌入式系统测试:从确定性验证到概率性评估的体系构建

软件测试是保障系统质量的关键环节,其核心在于验证功能是否符合预期。传统嵌入式测试聚焦于确定性逻辑,通过代码覆盖、边界值分析等方法确保时序、内存等指标的精确性。随着人工智能技术的普及,深度学习模型以其概率性输出和复杂非线性映射的特性,为嵌入式系统带来了新的能力,也彻底改变了测试的范式。AI模型的‘黑盒’特性使得测试目标从‘绝对正确’转向‘置信度满足’,并催生了准确率、F1分数等新指标。在工程实践中,这种融合要求测试体系必须扩展维度,将数据质量、模型量化损失、资源约束下的精度与性能权衡纳入核心考量。尤其在嵌入式

weixin_26766559的博客 383

Scrapy实现微博关键词爬虫(爬虫结果写入mongodb)

话不多说,上代码: spiders文件夹下的microBlogSpider.py里这样写: # -*- coding: utf-8 -*- import scrapy from scrapy import Spider, Request, FormRequest from scrapy.selector import Selector import datetime import random from blogSpider.items import microBlogItem, keyWordItem f

qq_39360565的博客 1779

Netty源码分析篇:Channel 的 register 操作

Netty源码分析篇:Channel 的 register 操作

java_rutu的博客 1324

Nano Banana 2哪里使用?Nano Banana 2提示词合集和使用教程

收集最强的Nano Banana 2提示词合集,以及Nano Banana 2哪里使用的教程,并且有10个免费使用渠道。

qq_42927225的博客 1999

Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector

Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public v...

weixin_30924239的博客 156

产品经理-面试题目--持续更新

答:面试官,你好,我叫XXX,来自于XXXX,我系统的学习过软件开发,也主动的积累了不少产品方面的实战经验,在学习期间,我自己独立的完成了2个深度项目,一个是仿抖音的短视频APP全流程设计,另外一个是电商后台系统的模块,通过这些项目,我不仅熟练使用Axure等工具做出高保真原型图和清晰的产品框架,也形成了数据驱动和用户优先的思维习惯,另外,我也注重拓展自己的能力边界,拿到了CAIE人工智能证书,我希望能把AI能力融入到产品场景,提升体验和效率,我非常喜欢产品工作带来的创造性和逻辑性,希望有机会加入贵公司。

m0_63966958的博客 958

美团外卖mtgsig 1.2逆向工程:从补环境到签名生成全解析

本文深入解析了美团外卖mtgsig 1.2签名算法的逆向工程全流程。从环境检测原理与补环境实战入手,详细拆解了签名JSON中每个关键字段的生成逻辑,并重点剖析了核心签名a2的生成步骤,涉及设备指纹、MD5、AES加密等关键技术。最后提供了构建签名生成器的实践思路与常见坑点,为理解复杂风控体系提供了完整的技术视角。

weixin_29324401的博客 1758

Netty入门 - Netty接收请求过程源码分析

启动服务器并绑定端口。使用处理连接和 I/O 操作。初始化Channel,配置处理器。接收新的连接请求并创建。将注册到EventLoop。触发read方法,读取数据并通过管道传递。处理器处理接收到的数据。通过分析 Netty 的源码,我们可以更好地理解其内部工作机制,为开发高性能网络应用提供坚实的基础。

qq_33240556的博客 530

dolphinscheduler-3.4.0

dolphinscheduler集群部署

docsz的博客 661

Netty 源码分析03 Channel 实现 AbstractChannel

注册方法 1.调用doRegister()方法进行注册,如果是NIO实现,如下 2.通过调用底层jdk的nio方法进行注册,本质上为放到channelArray this.channelArray[this.totalChannels] = var1; 3.注册后返回selectionKey,然后调用 pipeline.fireChannelRegistered(); 4....

郭传志的博客 483

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

netty源码分析 之七 transport(Unsafe)

来看下unsafe的接口定义 同样看你实现类, AbstractUnsafe 接着看起实现,统一的模板类,由子类来实现方法 接着看register 方法,其中有pipeline.fireChannelRegistered方法 private void register0(ChannelPromise promise) {

akfly的专栏 872

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Netty服务端的启动源码分析

ServerBootstrap的构造: 1 public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> { 2 private static final InternalLogger logger = InternalLoggerFactory.getI...

weixin_30662109的博客 108

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

netty源码分析之服务端启动全解析

background netty 是一个异步事件驱动的网络通信层框架,其官方文档的解释为 Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It great...

路漫漫其修远兮,吾将上下而求索 523

Netty源码------Channel的创建到底干了什么?

Channel的创建到底干了什么? 目录 Channel的创建到底干了什么? 1、先来了解一些什么是Netty的Channel 2、Netty的Channel创建的时序图 3、源码分析 Channel创建 Channel初始化 Channel注册 Channel绑定 总结 1、先来了解一些什么是Netty的Channel 什么是Channel? channel是一个管道,用于连接字节缓冲区Buf和另一端的实体,这个实例可以是Socket,也可以是File, 在Nio网络编程模型中

烟雨gps 1290

netty源码分析(十七)Netty线程模型深度解读与架构设计原则

上次分析到:public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNul

wzq6578702的专栏 3631

Netty学习笔记七Netty源码

Netty学习笔记七 七. Netty源码 写的比较乱,请见谅。 1. 启动剖析 Nio流程 // 1. 创建Selector Selector selector = Selector.open(); // 2. 创建serverSocketChannel ServerSocketChannel ssc = ServerSocketChannel.open(); // 3. 将serverSocketChannel注册到selector上 SelectionKey selectionKey

SunYang的博客 327

Netty源码分析--Channel注册&绑定端口(下)(七)

接下来,我们看到的就是两个非常重要的方法 就是processSelectedKeys() 和runAllTasks() 方法了。 selectionKey中ready的事件,如accept、connect、read、write等,由processSelectedKeys方法触发。属于I/O任务。 添加到taskQueue中...

weixin_30516243的博客 203

netty源码分析之服务端启动

ServerBootstrap与Bootstrap分别是netty中服务端与客户端的引导类,主要负责服务端与客户端初始化、配置及启动引导等工作,接下来我们就通过netty源码中的示例对ServerBootstrap与Bootstrap的源码进行一个简单的分析。首先我们知道这两个类都继承自AbstractBootstrap类 接下来我们就通过netty源码中ServerBootstrap的实例入手...

weixin_34356138的博客 81

Netty(六):NioServerSocketChannel源码解析

我们在Netty学习系列五的最后提出了一些问题还没得到回答,今天来通过学习NioServerSocketChannel的源码来帮我们找到之前问题的答案。 先看一下NioServerSocketChannel的继承结构。 AttributeMap接口及DefaultAttributeMap主要是提供了体检属性和获取属性的能力,便于我们为Channel绑定额外的属性。 Abstrac...

weixin_34110749的博客 160

网络编程17

Channel系列源码分析 NioServerSocketChannel NioSocketChannel channel----channel相关顶级接口 提供了很多跟网络通讯相关的操作,以及状态判定 read方法,读取到缓冲区,读取数据成功后,就会触发channel所属的pipeline进行handler的处理,触发channelRead,在各种handler中流转 flush方法,将我们要写到对端的数据,刷新到目标channel中,并发送给对方 config方法,获取当前channel的配置信

Markland_l的博客 168

04Channel 与 ChannelPipeline

Channel 与 ChannelPipeline相信大家都知道了, 在 Netty 中每个 Channel 都有且仅有一个 ChannelPipeline 与之对应, 它们的组成关系如下:head---->handler----->tail通过上图我们可以看到, 一个 Channel 包含了一个 ChannelPipeline, 而 ChannelPipeline 中又维护...

weixin_30781631的博客 206

五十三.网络(3)--区块同步getblocks消息

在节点互发了version消息后,就会发送getblocks消息,请求对方的区块以进行同步。

d3582077的专栏 189
上一篇: Netty源码分析---Connect与Bind对比总结
下一篇: EventLoop绑定到Channel的过程详解
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值