Netty 源码(二)NioEventLoop 之 Channel 注册
Netty 源码(二)NioEventLoop 之 Channel 注册
Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)
相关文章:

1. Channel 注册到 NioEventLoop
chnnel 初始化完成后就需要将其注册到对应的 NioEventLoop 上。
(1) NioEventLoopGroup 注册
// NioEventLoopGroup -> MultithreadEventLoopGroup
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
group() 返回的是上文配置的 childGroup 对象,即 NioEventLoopGroup,每个 NioEventLoopGroup 持有多个 NioEventLoop,next 依次返回一个 NioEventLoop。
(2) NioEventLoop 注册
// NioEventLoop -> SingleThreadEventLoop
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
可以看到最终调用对应 channel 的 unsafe 进行注册,下面看一下 unsafe 是如何注册的。
(3) Unsafe 注册
NioServerSocketChannel 的 unsafe = new NioMessageUnsafe(),而 NioMessageUnsafe 继承自 AbstractUnsafe。
// NioServerSocketChannel -> AbstractChannel.AbstractUnsafe
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
// 省略...
AbstractChannel.this.eventLoop = eventLoop;
// 同一个 channel 的注册、读、写等都在 eventLoop 完成,避免多线程的锁竞争
if (eventLoop.inEventLoop()) {
// 将 channel 注册到 eventLoop 上
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
// 省略...
}
}
}
private void register0(ChannelPromise promise) {
// 1. 确保 channel 的状态是 open,最终调用 ch.isOpen()
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// 2. channel 注册到 eventLoop 上
doRegister();
neverRegistered = false;
registered = true;
// 3. 此时 channel 已经注册到 eventLoop 上,此时需要将注册的 handler 绑定到 channel 上。eg: ChannelInitializer
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
// 4. channel 状态为 active 就需要触发 ChannelActive 事件或准备读
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
}
register 中最重要的方法是调用 doRegister 进行注册,该方法调用了 JDK 底层的代码进行注册。
(4) doRegister
// AbstractNioChannel
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 1. 调用 JDK NIO 将 channel 注册到 eventLoop 的 selector 上
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
// 2. 调用 selectNow 将注册的 channel 移除,不然有可能被缓存没有删除
// 移除后继续注册
eventLoop().selectNow();
selected = true;
} else {
// 3. JDK 提供的文档此时不会出现该异常,实际... JDK bug
throw e;
}
}
}
}
2. 注册感兴趣的事件
AbstractNioChannel#javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
register 方法注册 Java 原生 NIO 的 Channel 对象到 Selector 对象。但是为什么感兴趣事件的是 0 呢?正常情况下,对于服务端来说,需要注册 SelectionKey.OP_ACCEPT 事件。
(1) 注册方式是多态的,它即可以被 NIOServerSocketChannel 用来监听客户端的连接接入,也可以注册 SocketChannel 用来监听冉莹颖读或者写操作。
(2) 通过 SelectionKey#interestOps(int ops) 方法可以方便地修改监听操作位。所以,此处注册需要获取 SelectionKey 并给 AbstractNIOChannel 的成员变量 selectionKey 赋值。
- SelectionKey.OP_READ(1)
- SelectionKey.OP_WRITE(4)
- SelectionKey.OP_CONNECT(8)
- SelectionKey.OP_ACCEPT(16)
// AbstractChannel.AbstractUnsafe
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
// AbstractNioChannel
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
// 重新注册感兴趣的事件类型,readInterestOp 是 channel 初始化的时候传进来的
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
到此 NioServerSocketChannel 就重新注册上了 OP_ACCEPT 事件
每天用心记录一点点。内容也许不重要,但习惯很重要!
Netty 源码(二)NioEventLoop 之 Channel 注册的更多相关文章
- Netty源码—二、server启动(2)
我们在使用Netty的时候的初始化代码一般如下 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGro ...
- Netty源码—一、server启动(1)
Netty作为一个Java生态中的网络组件有着举足轻重的位置,各种开源中间件都使用Netty进行网络通信,比如Dubbo.RocketMQ.可以说Netty是对Java NIO的封装,比如ByteBu ...
- Netty 源码 Channel(二)核心类
Netty 源码 Channel(二)核心类 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一.Channel 类图 二. ...
- Netty 源码 Channel(二)主要类
Netty 源码 Channel(二)主要类 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一.Channel 类图 二. ...
- Netty 源码解析(二):Netty 的 Channel
本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty源码解析(一):开始 当前:Netty 源码解析(二): Netty 的 Channel ...
- Netty 源码 NioEventLoop(三)执行流程
Netty 源码 NioEventLoop(三)执行流程 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 上文提到在启动 N ...
- Netty 源码 NioEventLoop(一)初始化
Netty 源码 NioEventLoop(一)初始化 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) Netty 基于事件 ...
- Netty源码分析之NioEventLoop(三)—NioEventLoop的执行
前面两篇文章Netty源码分析之NioEventLoop(一)—NioEventLoop的创建与Netty源码分析之NioEventLoop(二)—NioEventLoop的启动中我们对NioEven ...
- Netty 源码解析(八): 回到 Channel 的 register 操作
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第八篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
随机推荐
- html -引入其他html页面
其他页面html为:ip.html 主页面代码 <body> <div id="ip"></div> </body> <scr ...
- redis 配置参数
redis配置参数说明: 属性 说明 daemonize 如果值是“yes”,则启动服务的时候是后台守护进程形式,如果值是“no”,则相反 pidfile 指定存储Redis进程号的文件路径 port ...
- Python之 ---成员修饰符
一:成员修饰符:分为共有成员和私有成员: 私有成员:__通过两个下滑线:无法直接访问,要访问只能间接访问: 如下我们定义了一个对象,里面有两个共有的成员变量,成员变量是共有的时候我们可以外部访问,如果 ...
- C/C++中#pragma once的使用
在C/C++中,为了避免同一个文件被include多次,有两种方式:一种是#ifndef方式,一种是#pragma once方式(在头文件的最开始加入). #ifndef SOME_UNIQUE_NA ...
- 如何解决make: Nothing to be done for `all' 的方法
正常情况下,当文件没有更新且已经编译过时,再次make就会报这个错误,表示文件未更新,不需要编译. 如果异常情况没有检测到更新文件,或者想要强制重新编译,只需要make clean,再次编译即可.
- JS中DOM以及BOM
一.bom对象 1screen对象 console.log(screen.width);// 屏幕宽度 console.log(screen.height);// 屏幕高度 console.log(s ...
- turret
turret - 必应词典 美['tʌrət]英['tʌrɪt] n.角楼:(战舰.飞机或坦克的)炮塔 网络塔楼:转塔:转台
- 【mysql】主从同步,事务等概念
问题: mysql用binary log来保证主从同步的可靠性和安全性,在mysql中,主从同步是异步线程和异步任务来保证的. (在这点上,其它存储引擎有另外的选项,比如mongoDB和Elastic ...
- TCP/IP中的四元组、五元组、七元组
四元组:源IP地址.目的IP地址.源端口.目的端口 五元组:源IP地址.目的IP地址.源端口.目的端口.传输层协议 七元组:源IP地址.目的IP地址.源端口.目的端口.传输层协议,服务类型以及接口索引
- 8.17 纯css画一个着重号图标
今天看到一个同事写的着重号图标,我以为是图片,仔细一看,是span标签!哇!!学习一下哈哈 图标长这样: CSS代码: .hint{ display: inline-block; width: 20p ...