Netty源码学习2——NioEventLoop的执行
零丶引入
在《Netty源码学习1——NioEventLoopGroup的初始化》中,我们学习了NioEventLoopGroup和NioEventLoop的初始化,在下面netty服务端启动的demo中

会在ServerBootStrap中指定Channel为Nio类型的Channel,然后启动的时候绑定端口,之前我们解释道NioEventLoop是事件循环,这里的事件是指客户端连接,数据准备就绪等IO事件,循环是指会有一个线程不断的处理任务。so 这篇我们一起看下NioEventLoop是如何产生IO事件并执行各种任务的。
一丶NioEventLoop的初始化
这部分在《Netty源码学习1——NioEventLoopGroup的初始化)》中进行了说明,其中比较有趣的是Netty对JDK原生Selector的优化,将原本基于HashSet的SelectionKey集合,修改为基于数组的SelectionKey集合,优化了迭代和修改的效率,再次就不在过多赘述了。
二丶NioEventLoop的启动
NioEventLoop中存在一个run方法,该方法是一个死循环,这便是事件循环中的循环,它由一个线程专门去执行,我们先分析下NioEventLoop#run方法是什么时候开始执行的。

如上分别是客户端和服务端启动部分的源码,在bind 和 connect中都会触发——Channel的初始化+Channel的注册。
Channel的初始化:由对应的Factory进行创建(默认使用反射)
Channel的注册:这里的注册将调用NioEventLoopGroup#register方法,

这里自然是通过EventExecutorChooser#next方法选择一个NioEventLoop进行注册


在excute方法中会将EventLoop#run方法包装成一个Runnable提交到线程池中,这里的线程池是被包装后的ThreadPerTaskExecutor,每一个任务的提交都会新建一个线程去执行

因此可以任务NioEventLoop#run方法由一个线程单独去执行
三丶NioEventLoop#run
1.SelectStrategy控制循环
和Tomcat的Poller类似,NioEventLoop需要使用Selector去进行IO多路复用,Netty抽象出SelectStrategy使用其calculateStrategy来控制循环


其中selectNow即使用JDK Selector#selectNow方法进行非阻塞select

DefaultSelectStrategy#calculateStrategy的逻辑是,如果当前EventLoop中存在任务那么直接返回就绪的就绪SelectionKey数目,如果不存哎任务,那么返回常量SELECT(-1),根据返回的数字会进入不同的分支。

2.执行阻塞select

NioEventLoop支持调度任务,因此内部使用了优先队列保存调度任务,并且调度任务具备一个deadlineNanos属性


3.执行任务
NioEventLoop支持ioRatio指定IO事件的处理事件,和其他任务执行时间的比例

其中处理IO时间的部分涉及到Netty的ChannelPipeline的执行,这部分在后续的博客中进行讲解
3.1 runAllTasks
无参的runAllTask会在IO事件处理结束后执行,首先会将scheduledTaskQueue中的任务(未到执行时间的任务,or deadlineNanos早于当前时间的任务 不会转移到taskQueue)转移到taskQueue,然后执行taskQueue中的任务,执行完taskQueue的任务后,执行tailTasks中的任务

3.2 runAllTasks(超时时间)
大流程和上面runAllTasks类似,依旧是将调度任务从scheduledTaskQueue转移到taskQueue,然后执行taskQueue任务,然后执行tailTasks中的任务

感觉比较有趣的是,对调度任务的处理,移动到taskQueue后并不会再次判断其deadlineNanos和当前时间的关系,不会再关注其是否过期。
3.3 处理NIO空轮询的bug
Java NIO在linux下基于epoll,但是因为poll和epoll对于突然中断的连接socket会对返回的eventSet事件集合置为EPOLLHUP或者EPOLLERR,eventSet事件集合发生了变化,这会导致Selector会被唤醒,唤醒之后却没有感兴趣的事件,导致IO事件处理的循环不会执行,陷入到循环中.

下面我们看看netty是如何处理此bug的


可以看到Netty通过重新构建Selector的方式去规避空轮询的bug,其中rebuildSelector最终调用rebuildSelector0,会将原本selector中的SelectionKey拷贝到新的selector,然后关闭旧的Selector
四丶总结
此节我们学习了NioEventLoop的执行原理,本质上还是通过Selector进行IO多路复用,理解了什么是事件循环,其中的事件指通过IO多路复用监听多路IO事件,循环是指循环的监听IO事件,并处理IO事件和其他任务,但是对于IO事件的处理我们并未做更多的学习,这部分将在下一篇中进行学习。
Netty源码学习2——NioEventLoop的执行的更多相关文章
- Netty源码分析之NioEventLoop(三)—NioEventLoop的执行
前面两篇文章Netty源码分析之NioEventLoop(一)—NioEventLoop的创建与Netty源码分析之NioEventLoop(二)—NioEventLoop的启动中我们对NioEven ...
- 【Netty源码解析】NioEventLoop
上一篇博客[Netty源码学习]EventLoopGroup中我们介绍了EventLoopGroup,实际说来EventLoopGroup是EventLoop的一个集合,EventLoop是一个单线程 ...
- Netty 源码学习——EventLoop
Netty 源码学习--EventLoop 在前面 Netty 源码学习--客户端流程分析中我们已经知道了一个 EventLoop 大概的流程,这一章我们来详细的看一看. NioEventLoopGr ...
- Netty 源码学习——客户端流程分析
Netty 源码学习--客户端流程分析 友情提醒: 需要观看者具备一些 NIO 的知识,否则看起来有的地方可能会不明白. 使用版本依赖 <dependency> <groupId&g ...
- Netty源码学习系列之4-ServerBootstrap的bind方法
前言 今天研究ServerBootstrap的bind方法,该方法可以说是netty的重中之重.核心中的核心.前两节的NioEventLoopGroup和ServerBootstrap的初始化就是为b ...
- 【Netty源码学习】ServerBootStrap
上一篇博客[Netty源码学习]BootStrap中我们介绍了客户端使用的启动服务,接下来我们介绍一下服务端使用的启动服务. 总体来说ServerBootStrap有两个主要功能: (1)调用父类Ab ...
- 【Netty源码学习】DefaultChannelPipeline(三)
上一篇博客中[Netty源码学习]ChannelPipeline(二)我们介绍了接口ChannelPipeline的提供的方法,接下来我们分析一下其实现类DefaultChannelPipeline具 ...
- 【Netty源码学习】ChannelPipeline(一)
ChannelPipeline类似于一个管道,管道中存放的是一系列对读取数据进行业务操作的ChannelHandler. 1.ChannelPipeline的结构图: 在之前的博客[Netty源码学习 ...
- 【Netty源码学习】EventLoopGroup
在上一篇博客[Netty源码解析]入门示例中我们介绍了一个Netty入门的示例代码,接下来的博客我们会分析一下整个demo工程运行过程的运行机制. 无论在Netty应用的客户端还是服务端都首先会初始化 ...
- mybatis源码学习:插件定义+执行流程责任链
目录 一.自定义插件流程 二.测试插件 三.源码分析 1.inteceptor在Configuration中的注册 2.基于责任链的设计模式 3.基于动态代理的plugin 4.拦截方法的interc ...
随机推荐
- 2021-11-06:3的幂。给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。整数 n 是 3 的幂次方需满足:存在整数 x 使得 n ==
2021-11-06:3的幂.给定一个整数,写一个函数来判断它是否是 3 的幂次方.如果是,返回 true :否则,返回 false .整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == ...
- 2021-09-12:请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。函数 myAtoi(string
2021-09-12:请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数).函数 myAtoi(string ...
- dates()datetimes()查询都有哪些日期
dates()查询都有哪些日期 created_at是列名,year,是要查询的参数,order为排序方式 Course.objects.dates('created_at','year',order ...
- MMCM/PLL VCO
输入输出时钟频率,input 322.265625Mhz, output 312.5Mhz 对于使用MMCM与PLL的不同情况,虽然输入输出频率是一样的,但是,分/倍频系数是不同的,不能使用同一套参数 ...
- phpstudy-sqlilabs-less-14
题目:POST - Double Injection - Single quotes- String - with twist 和上关一模一样 uname=1"or 1=1 #&pa ...
- vscode 配置代码自动格式化加修复
子曰:"工欲善其事,必先利其器", 编码必须的就是有一个顺手的ide,然而光有还不行,还要懂得配置,毕竟不同的团队代码规范不同,如目前用得较多的就是eslint,今天就顺便记录下v ...
- 运行和编译时期资源加载的不同【vue】
开发语言都有编译和运行两个阶段,很多时候这个也会带来许多bug 如:一个项目在开发阶段测试没有问题,然上线发布后就会有这样那样的问题,譬如说图片的加载,静态数据(js,css,json)读取错误 一 ...
- PlayWright(一)
1.如何安装? 安装playwright只需要一条命令,就是pip安装命令,命令如下: pip install playwright 注:playwright需要Python3.7或更新的版本 2.然 ...
- WPF 自定义控件 二次渲染 问题记录
问题 将多个自定义控件加载到到一个页面的Grid上显示.然后突然将一个控件从Grid里面清除,控件依然在后台处理数据. 过段时间再加入Grid.然后一些已经改变的页面属性就消失了. 原因 经过查找是一 ...
- 自然语言处理 Paddle NLP - 情感分析技术及应用SKEP-实践
Part A. 情感分析任务 众所周知,人类自然语言中包含了丰富的情感色彩:表达人的情绪(如悲伤.快乐).表达人的心情(如倦怠.忧郁).表达人的喜好(如喜欢.讨厌).表达人的个性特征和表达人的立场等等 ...