Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用
Netty源码分析第一章:Netty启动流程
第四节:注册多路复用
回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继续分析
回到上一小节的代码:
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//创建channel
channel = channelFactory.newChannel();
//初始化channel
init(channel);
} catch (Throwable t) {
//忽略非关键代码
}
//注册channel
ChannelFuture 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() {
@Override
public 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, 因为链路没完成
本小节梳理了有注册多路复用的相关逻辑, 同学们可以跟着代码自己走一遍以加深印象
Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用的更多相关文章
- Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector
Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void ...
- Netty源码分析第3章(客户端接入流程)---->第5节: 监听读事件
Netty源码分析第三章: 客户端接入流程 第五节: 监听读事件 我们回到AbstractUnsafe的register0()方法: private void register0(ChannelPro ...
- Netty源码分析第3章(客户端接入流程)---->第1节: 初始化NioSockectChannelConfig
Netty源码分析第三章: 客户端接入流程 概述: 之前的章节学习了server启动以及eventLoop相关的逻辑, eventLoop轮询到客户端接入事件之后是如何处理的?这一章我们循序渐进, 带 ...
- Netty源码分析第3章(客户端接入流程)---->第2节: 处理接入事件之handle的创建
Netty源码分析第三章: 客户端接入流程 第二节: 处理接入事件之handle的创建 上一小节我们剖析完成了与channel绑定的ChannelConfig初始化相关的流程, 这一小节继续剖析客户端 ...
- Netty源码分析第3章(客户端接入流程)---->第3节: NioSocketChannel的创建
Netty源码分析第三章: 客户端接入流程 第三节: NioSocketChannel的创建 回到上一小节的read()方法: public void read() { //必须是NioEventLo ...
- Netty源码分析第1章(Netty启动流程)---->第1节: 服务端初始化
Netty源码分析第一章: Server启动流程 概述: 本章主要讲解server启动的关键步骤, 读者只需要了解server启动的大概逻辑, 知道关键的步骤在哪个类执行即可, 并不需要了解每一步的 ...
- Netty源码分析第1章(Netty启动流程)---->第2节: NioServerSocketChannel的创建
Netty源码分析第一章: Server启动流程 第二节:NioServerSocketChannel的创建 我们如果熟悉Nio, 则对channel的概念则不会陌生, channel在相当于一个通 ...
- Netty源码分析第1章(Netty启动流程)---->第3节: 服务端channel初始化
Netty源码分析第一章:Netty启动流程 第三节:服务端channel初始化 回顾上一小节的initAndRegister()方法: final ChannelFuture initAndRe ...
- Netty源码分析第1章(Netty启动流程)---->第5节: 绑定端口
Netty源码分析第一章:Netty启动步骤 第五节:绑定端口 上一小节我们学习了channel注册在selector的步骤, 仅仅做了注册但并没有监听事件, 事件是如何监听的呢? 我们继续跟第一小节 ...
随机推荐
- ubuntu16.04安装visual-studio-code
微软主页的安装说明,https://code.visualstudio.com/docs/setup/linux 有一点英语基础就能看懂,写的很好,一切以官方文档为主 方法一:可以使用umake ...
- virtualbox+vagrant学习-2(command cli)-15-vagrant resume命令
Resume 格式: vagrant resume [vm-name] 这将恢复先前挂起的vagrant托管计算机,可能与suspend命令一起使用. 默认情况下,配置的预配置程序在运行该命令时将不再 ...
- regex_match
原型:bool regex_match(InputSequence[,MatchResults] , Regex[ , Flags]); 当模式匹配整个输入序列成功时,返回的是true,否则返回fal ...
- 列表中不限制宽度,hover时,字体font-weight:bold,防止抖动
项目一个小问题困扰了很久,在一个没有限制宽度的列表容器中,如果给hover时,给字体➕'font-wieght:bold'容器就会变宽,然后移动的下一个容器,就会出现抖动,这样很是影响用户体验,于是在 ...
- 深入理解C/C++混合编程优秀博文赏析与学习
1.深入理解C/C++混合编程 2.C/C++ 混合编程 互相调用 3.C代码中如何调用C++ C++中如何调用C 4.C和C++混合编程
- 关于 MFRC522引脚功能图
MFRC522是属于13.56mhz芯片.另外SI522也是13.56mhz芯片,SI522 PIN对PIN完全兼容MFRC522,并且软硬件兼容,且引脚功能图都是一样的,功能方面比MFRC522多A ...
- Spring整合MyBatis(四)MapperFactoryBean 的创建
摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 目录 一.MapperFactoryBean的初始化 二.获取 Map ...
- Nginx代理
Nginx 介绍:高性能的http服务器和反向代理(请求通过反向代理之后,访问服务器端的逻辑)如下图所示: Ningx的作用 负载均衡 所谓负载就是服务器各项技术所承受的压力 均衡,平均分配压力(物理 ...
- linux学习第十七天(NFS、AUTOFS文件共享配置,DNS配置)
一.NFS(网络文件系统,实现linux系统上文件共享) 服务器配置 yum install nfs-utils (安装NFS软件包) iptables -F (清空防火墙) service ip ...
- jQuery hide() 参数callback回调函数执行问题
$("#b").click(function() { $("div").hide(1000,bbb); //-------------1 bbb是一个函数,但这 ...