Netty源码分析第1章(Netty启动流程)----第4节: 注册多路复用
Netty源码分析第一章:Netty启动流程
第四节:注册多路复用
回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继续分析
回到上一小节的代码:
final ChannelFuture initAndRegister() {Channel channel = null;try {//创建channelchannel = channelFactory.newChannel();//初始化channel init(channel);} catch (Throwable t) {//忽略非关键代码 }//注册channelChannelFuture regFuture = config().group().register(channel);//忽略非关键代码return regFuture; }
我们讲完创建channel和初始化channel的关键步骤, 我们继续跟注册channel的步骤:
ChannelFuture regFuture = config().group().register(channel);
其中, 重点关注下register(channel)这个方法, 这个方法最终会调用到AbstractChannel中内部类AbstractUnsafe的register()方法, 具体如何调用到这个方法, 可以简单带大家捋一下
首先看下config()方法, 由于是ServerBootstrap调用的, 所以我们跟进去:
public final ServerBootstrapConfig config() {return config; }
返回的config是ServerBootrap的成员变量config:
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
跟到ServerBootstrapConfig的构造方法:
ServerBootstrapConfig(ServerBootstrap bootstrap) {super(bootstrap); }
继续跟到其父类AbstractBootstrapConfig的构造方法:
protected AbstractBootstrapConfig(B bootstrap) {this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap"); }
我们发现我们创建的ServerBootstrap作为参数初始化了其成员变量bootstrap
回到initAndRegister()方法:
config()返回的是ServerBootstrapConfig对象
再继续跟到其group()方法:
public final EventLoopGroup group() {return bootstrap.group(); }
这里调用Bootstrap的group()方法:
public final EventLoopGroup group() {return group; }
这里返回了AbstractBootstrap的成员变量group, 我们回顾下第一小节, 还记得AbstractBootstrap的group(EventLoopGroup group)方法吗?
public B group(EventLoopGroup group) {this.group = group;return (B) this; }
group(EventLoopGroup group)方法初始化了我们boss线程, 而group()返回了boss线程, 也就是说 config().group().register(channel) 中的register()方法是boss线程对象调用的, 由于我们当初初始化的是NioEventLoopGroup, 因此走的是NioEventLoopGroup的父类的MultithreadEventLoopGroup的register()方法
跟到MultithreadEventLoopGroup的register()方法:
public ChannelFuture register(Channel channel) {return next().register(channel); }
这里的代码看起来有点晕, 没关系, 以后会讲到, 现在可以大概做个了解, NioEventLoopGroup是个线程组, 而next()方法就是从线程组中选出一个线程, 也就是NioEventLoop线程, 所以这里的next()方法返回的是NioEventLoop对象, 其中register(channel)最终会调用NioEventLoop的父类SingleThreadEventLoop的register(channel)方法
跟到SingleThreadEventLoop的register(channel)方法:
public ChannelFuture register(Channel channel) {return register(new DefaultChannelPromise(channel, this)); }
其中DefaultChannelPromise类我们之后也会讲到
我们先跟到register(new DefaultChannelPromise(channel, this)):
public ChannelFuture register(final ChannelPromise promise) {ObjectUtil.checkNotNull(promise, "promise");promise.channel().unsafe().register(this, promise);return promise; }
channel()会返回我们初始化的NioServerSocketChannel, unsafe()会返回我们创建channel的时候初始化的unsafe对象
跟进去看AbstractChannel的unsafe()的实现:
public Unsafe unsafe() {return unsafe; }
这里返回的unsafe, 就是我们初始化channel创建的unsafe
回顾下第二小节channel初始化的步骤:
protected AbstractChannel(Channel parent) {this.parent = parent;id = newId();unsafe = newUnsafe();pipeline = newChannelPipeline(); }
我们看unsafe的初始化:unsafe=newUnsafe()
跟到newUnsafe()中, 我们之前讲过NioServerSokectChannel的父类是AbstractNioMessageChannel, 所以会调用到到AbstractNioMessageChannel类中的newUnsafe()
跟到AbstractNioMessageChannel类中的newUnsafe():
protected AbstractNioUnsafe newUnsafe() {return new NioMessageUnsafe(); }
我们看到这里创建了NioMessageUnsafe()对象, 所以在 promise.channel().unsafe().register(this, promise) 代码中, unsafe()是返回的NioMessageUnsafe()对象, 最后调用其父类AbstractUnsafe(也就是AbstractChannel的内部类)的register()方法,
简单介绍下unsafe接口, unsafe顾名思义就是不安全的, 因为很多对channel的io方法都定义在unsafe中, 所以netty将其作为内部类进行封装, 防止被外部直接调用, unsafe接口是Channel接口的内部接口, unsafe的子类也分别封装在Channel的子类中, 比如我们现在剖析的register()方法, 就是封装在AbstractChannel类的内部类AbstractUnsafe中的方法, 有关Unsafe和Channel的继承关系如下:
1-4-1
以上内容如果不明白没有关系, 有关NioEventLoop相关会在后面的章节讲到, 目前我们只是了解是如何走到AbstractUnsafe类的register()即可
我们继续看看register()方法:
public final void register(EventLoop eventLoop, final ChannelPromise promise) {//代码省略//所有的复制操作, 都交给eventLoop处理(1)AbstractChannel.this.eventLoop = eventLoop;if (eventLoop.inEventLoop()) {register0(promise);} else {try {eventLoop.execute(new Runnable() {@Overridepublic void run() {//做实际主注册(2) register0(promise);}});} catch (Throwable t) {//代码省略 }} }
我们跟着注释的步骤继续走, 第一步, 绑定eventLoop线程:
AbstractChannel.this.eventLoop = eventLoop;
eventLoop是AbstractChannel的成员变量, 有关eventLoop, 我们会在绪章节讲到, 这里我们只需要知道, 每个channel绑定唯一的eventLoop线程, eventLoop线程和channel的绑定关系就是在这里展现的
再看第二步, 做实际注册:
我们先看if判断, if(eventLoop.inEventLoop())
这里是判断是不是eventLoop线程, 显然我们现在是main()方法所在的线程, 所以走的else, eventLoop.execute()是开启一个eventLoop线程, 而register0(promise)就是再开启线程之后, 通过eventLoop线程执行的, 这里大家暂时作为了解
我们重点关注register0(promise), 跟进去:
private void register0(ChannelPromise promise) {try {//做实际的注册(1) doRegister();neverRegistered = false;registered = true;//触发事件(2) pipeline.invokeHandlerAddedIfNeeded();safeSetSuccess(promise);//触发注册成功事件(3) pipeline.fireChannelRegistered();if (isActive()) {if (firstRegistration) {//传播active事件(4) pipeline.fireChannelActive();} else if (config().isAutoRead()) {beginRead();}}} catch (Throwable t) {//省略代码 } }
我们重点关注doRegister()这个方法
doRegister()最终会调用AbstractNioChannel的doRegister()方法:
protected void doRegister() throws Exception {boolean selected = false;for (;;) {try {//jdk底层的注册方法//第一个参数为selector, 第二个参数表示不关心任何事件selectionKey = javaChannel().register(eventLoop().selector, 0, this);return;} catch (CancelledKeyException e) {//省略代码 }} }
我们终于看到和java底层相关的方法了
跟到javaChannel()的方法中:
protected SelectableChannel javaChannel() {return ch; }
这个ch, 就是本章第二小节创建NioServerSocketChannel中初始化的jdk底层ServerSocketChannel
这里register(eventLoop().selector, 0, this)方法中eventLoop().selector, 是获得每一个eventLoop绑定的唯一的selector, 0代表这次只是注册, 并不监听任何事件, this是代表将自身(NioEventLoopChannel)作为属性绑定在返回的selectionKey当中, 这个selectionKey就是与每个channel绑定的jdk底层的SelectionKey对象, 熟悉nio的小伙伴应该不会陌生, 这里不再赘述
回到register0(ChannelPromise promise)方法, 我们看后续步骤:
步骤(2)是触发handler的需要添加事件, 事件传递的内容我们将在后续课程详细介绍, 这里不必深究
步骤(3)是触发注册成功事件(3), 同上
步骤(4)是传播active事件(4), 这里简单强调一下, 这里的方法pipeline.fireChannelActive()第一个注册是执行不到的, 因为isActive()会返回false, 因为链路没完成
本小节梳理了有注册多路复用的相关逻辑, 同学们可以跟着代码自己走一遍以加深印象
上一节: 服务端Channel的初始化
下一节: 绑定端口
转载于:https://www.cnblogs.com/xiangnan6122/p/10202849.html
Netty源码分析第1章(Netty启动流程)----第4节: 注册多路复用相关推荐
- Netty源码分析第7章(编码器和写数据)----第2节: MessageToByteEncoder
Netty源码分析第7章(编码器和写数据)---->第2节: MessageToByteEncoder Netty源码分析第七章: Netty源码分析 第二节: MessageToByteEnc ...
- Netty源码分析第6章(解码器)----第4节: 分隔符解码器
Netty源码分析第6章(解码器)---->第4节: 分隔符解码器 Netty源码分析第六章: 解码器 第四节: 分隔符解码器 基于分隔符解码器DelimiterBasedFrameDecode ...
- Netty源码分析第5章(ByteBuf)----第5节: directArena分配缓冲区概述
Netty源码分析第5章(ByteBuf)---->第5节: directArena分配缓冲区概述 Netty源码分析第五章: ByteBuf 第五节: directArena分配缓冲区概述 上 ...
- 【SemiDrive源码分析】【X9芯片启动流程】12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析
[SemiDrive源码分析][X9芯片启动流程]12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析 一.s ...
- 【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍
[SemiDrive源码分析][X9芯片启动流程]08 - X9平台 lk 目录源码分析 之 目录介绍 一./rtos/lk/ 目录结构分析 1.1 /rtos/lk_boot/ 目录结构分析 1.2 ...
- 【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇
[SemiDrive源码分析][X9芯片启动流程]21 - MailBox 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇 一.Mailbox for Linux 驱动框架分 ...
- 【SemiDrive源码分析】【X9芯片启动流程】20 - MailBox 核间通信机制介绍(代码分析篇)之 MailBox for RTOS 篇
[SemiDrive源码分析][X9芯片启动流程]20 - MailBox 核间通信机制介绍(代码分析篇)之 MailBox for RTOS 篇 一.Mailbox for RTOS 源码分析 1. ...
- 【SemiDrive源码分析】【X9芯片启动流程】30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一)
[SemiDrive源码分析][X9芯片启动流程]30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一) 一.Android Kernel 启动流程分析 ...
- 【SemiDrive源码分析】【X9芯片启动流程】19 - MailBox 核间通信机制介绍(理论篇)
[SemiDrive源码分析][X9芯片启动流程]19 - MailBox 核间通信机制介绍(理论篇) 一.核间通信 二.核间通信软件架构 三.Mailbox 设备驱动 3.1 Mailbox for ...
最新文章
- 新型冠状病毒心理防护手册(大众版)
- linux无法设置日期 不允许的操作,如何解决系统时间无法修改的问题
- 迅雷离线工具 小众雷友 测试版
- Python:对字符串匹配算法的分析
- CVPR 2018 DEDT:《Efficient Diverse Ensemble for Discriminative Co-Tracking》论文笔记
- 软件项目组织管理(六)项目时间管理
- python获取当前路径的方法_Python获取脚本所在目录的正确方法【转】
- 论文浅尝 | 具有图卷积网络和顺序注意力机制的应用于目标导向的对话系统
- 计算机雕刻教学设计,教学设计季花的雕刻方法.doc
- Caffe学习记录(十一) ICNet分割网络学习
- oracle一步一步01
- smale学习之数学表达式(day4)
- JAXB XML和Bean互相转换
- 麒麟服务器操作系统V4,银河麒麟服务器操作系统V4
- bsod错误代码。_如何解决BSOD(蓝屏死机)和Windows Stop错误?
- Android资源应用与适配标准
- 实用技巧----百度绘制函数图像
- MATLAB机器人可视化
- Python自然语言处理:文档相似度计算(gensim.models)
- python 通过调取百度接口进行图片OCR文字识别 高识别率