Selector空轮询
JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该BUG发生概率降低了一些而已,它并没有被根本解决。该BUG以及与该BUG相关的问题单可以参见以下链接内容。
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=2147719
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6403933
参考:https://github.com/netty/netty/issues/327
参考:https://www.jianshu.com/p/d0f06b13e2fb
参考:http://blog.jobbole.com/105564/
参考:http://blog.csdn.net/xyls12345/article/details/26571699
Selector BUG出现的原因
若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%
Netty的解决办法
- 对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数,
- 若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug。
- 重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。
参考:http://blog.csdn.net/baiye_xing/article/details/73351330
前面讲到了epoll的一些机制,与select和poll等传统古老的IO多路复用机制的一些区别,这些区别实质可以总结为一句话,
就是epoll将重要的基于事件的fd集合放在了内核中来完成,因为内核是高效的,所以很多关于fd事件监听集合的操作也是高效的,
不方便的就是,因为在内核中,所以我们需要通过系统调用来调用关于fd操作集合,而不是直接自己攒一个。
如果在linux中,epoll在JDK6中还需要配置,在后续的版本中为JDK的NIO提供了默认的实现,但是epoll在JDK中的实现却是漏洞百出的,
bug非常的多,比较容易复现并且被众多人诟病的就是epoll轮询的处理方法。
sun的bug列表为:
JDK-6670302 (se) NIO selector wakes up with 0 selected keys infinitely [lnx 2.4]
JDK-6670302 : (se) NIO selector wakes up with 0 selected keys infinitely [lnx 2.4]
===》这个bug的描述内容为,在NIO的selector中,即使是关注的select轮询事件的key为0的话,NIO照样不断的从select本应该阻塞的
情况中wake up出来,也就是下图中的红色阻塞的部分:
然后,因为selector的select方法,返回numKeys是0,所以下面本应该对key值进行遍历的事件处理根本执行不了,又回到最上面的while(true)循环,循环往复,不断的轮询,直到linux系统出现100%的CPU情况,其它执行任务干不了活,
最终导致程序崩溃。
==》从这个bug上来看,这个绝对是JDK中的问题,select方法就应该是阻塞的,没有key事件过来,那么就不应该返回,和应用程序的写法没有任何的关系,与之相差不多的一个bug给出了解决的方案:
JDK-6403933 (se) Selector doesn't block on Selector.select(timeout) (lnx)
JDK-6403933 : (se) Selector doesn't block on Selector.select(timeout) (lnx)
这个bug的意思基本上和前面的JDK-6670302相差不大,也是Selector不阻塞,前一个bug说明的是最终的现象,
这个JDK-6403933的bug说出了实质的原因:
具体解释为,在部分Linux的2.6的kernel中,poll和epoll对于突然中断的连接socket会对返回的eventSet事件集合置为POLLHUP,也可能是POLLERR,eventSet事件集合发生了变化,这就可能导致Selector会被唤醒。==》这是与操作系统机制有关系的,JDK虽然仅仅
是一个兼容各个操作系统平台的软件,但很遗憾在JDK5和JDK6最初的版本中(严格意义上来将,JDK部分版本都是),这个问题并没有解决,而将这个帽子抛给了操作系统方,这也就是这个bug最终一直到2013年才最终修复的原因,最终影响力太广。
修复的方法,在这个bug中已经提到了:
上面是第一个建议,首先将SelectKey去除掉,然后“刷新”一下Selector,刷新的方式也就是调用Selector.selectNow方法,
这个示意的代码如下:
这段代码意味着重置,首先将SelectionKey注销掉,然后重新调用非阻塞的selectNow来让Selector换取“新生”。
这种修改方式就是grizzly的commiteer们最先进行修改的,并且通过众多的测试说明这种修改方式大大降低了JDK NIO的问题。
但是,这种修改仍然不是可靠的,一共有两点:
1.多个线程中的SelectionKey的key的cancel,很可能和下面的Selector.selectNow同时并发,如果是导致key的cancel后运行很可能没有效果
2.与其说第一点使得NIO空转出现的几率大大降低,经过Jetty服务器的测试报告发现,这种重复利用Selector并清空SelectionKey的改法很可能没有任何的效果,
最终的终极办法是创建一个新的Selector
具体的Jetty服务器的分析地址为:
Jetty/Feature/JVM NIO Bug
Jetty首先定义两了-D参数:
- org.mortbay.io.nio.JVMBUG_THRESHHOLD, defaults to 512 and is the number of zero select returns that must be exceeded in a period.
- org.mortbay.io.nio.MONITOR_PERIOD defaults to 1000 and is the period over which the threshhold applies.
第一个参数是select返回值为0的计数,第二个是多长时间,整体意思就是控制在多长时间内,如果Selector.select不断返回0,说明进入了JVM的bug的模式
那么,Jetty这时候就有所作为了,我们看到Jetty的具体的代码如下:
首先,根据-D参数判断是否进入了JAVA NIO空转的bug模式,一个是判断时间,一个是判断次数,次数通过-jvmBug作为计数器进行统计;如果一旦确定是bug,可以看到上述代码为了防止并发出现,加了Sychronized锁,接着开启一个新的Selector,并将原有的SelectionKey的事件全部转移到了新的Selector中,最后将-jvmBug计数器置0;
==》这种处理方法要保险的多,基本上不会有任何的问题了,
即使上述的处理方式,对应极少的linux环境和JDK的版本,仍会出现一些问题,这主要是因为网络中断的间隔时间太短造成的,需要给内核一定的时钟周期进行缓冲,而上述的Jetty的org.mortbay.io.nio.BUSY_PAUSE这个参数就是起到间隔的作用,间隔多少微秒再调用Select,这样基本上能最大程度上避免上述问题出现了。
从上面Jetty各种处理方法来看,基本能屏蔽低版本JDK和操作系统的epoll的影响,让NIO可以无忧运行。当然,对于NIO框架也是修正了这些错误,前面提到的Griizzly和Netty都对这个问题采取了响应的策略。
以Netty为例,具体位置在NioSelector的实现类AbsNioSelector中,思路和Jetty的处理方式几乎是一样的,就是netty讲重建Selector的过程抽取成了一个方法,叫做rebuildSelector,可以看看其方法:
public void rebuildSelector() {if (!inEventLoop()) {execute(new Runnable() {@Overridepublic void run() {rebuildSelector();}});return;}final Selector oldSelector = selector;final Selector newSelector;if (oldSelector == null) {return;}try {newSelector = openSelector();} catch (Exception e) {logger.warn("Failed to create a new Selector.", e);return;}// Register all channels to the new Selector.int nChannels = 0;for (;;) {try {for (SelectionKey key: oldSelector.keys()) {Object a = key.attachment();try {if (!key.isValid() || key.channel().keyFor(newSelector) != null) {continue;}int interestOps = key.interestOps();key.cancel();SelectionKey newKey = key.channel().register(newSelector, interestOps, a);if (a instanceof AbstractNioChannel) {// Update SelectionKey((AbstractNioChannel) a).selectionKey = newKey;}nChannels ++;} catch (Exception e) {logger.warn("Failed to re-register a Channel to the new Selector.", e);if (a instanceof AbstractNioChannel) {AbstractNioChannel ch = (AbstractNioChannel) a;ch.unsafe().close(ch.unsafe().voidPromise());} else {@SuppressWarnings("unchecked")NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;invokeChannelUnregistered(task, key, e);}}}} catch (ConcurrentModificationException e) {// Probably due to concurrent modification of the key set.continue;}break;}selector = newSelector;try {// time to close the old selector as everything else is registered to the new oneoldSelector.close();} catch (Throwable t) {if (logger.isWarnEnabled()) {logger.warn("Failed to close the old Selector.", t);}}logger.info("Migrated " + nChannels + " channel(s) to the new Selector.");}
基本上类似,这里就不再缀余。
分析到这里,可以看到为什么NIO框架如Netty,Grizzly,还有最近的炒得很热的Jboss的UnderTow,NIO远远不止这篇文章分析得这一个,还有很多,大可在JDK官网上去查,而这些框架都将NIO的很多不好用的问题,bug隐藏起来了,并加上诸如限流,字符转换,基于设计模式等特性,让开发人员更好的编写高并发的程序,而不用过多的网络的关注与细节。
由此可见,现在JAVA真是越来越危机了,从前几年的SSH把java ee给替换掉,到现在jdk都时不时冒出一个bug来,而且最近JDK8中的一个bug大有超过这个bug之势,jcp社区确实需要好好反省了,要不然java没落了,一干程序员又得下岗再就业了。
总结:
NIO的空转bug历史悠久流传广泛,应用服务器的前端框架一般都采取换一个新Selector的方式对此进行处理,屏蔽掉了JDK5/6的问题,但对于此问题来讲,还是尽量将JDK的版本更新到最新,或者使用NIO框架如Netty,Grizzly等进行研发,以免出更多的问题。
epoll bug CPU空轮询
SUN在解决该BUG的问题上不给力,只能从NIO框架层面进行问题规避,下面我们看下Netty是如何解决该问题的。
Netty的解决策略:
1) 根据该BUG的特征,首先侦测该BUG是否发生;
2) 将问题Selector上注册的Channel转移到新建的Selector上;
3) 老的问题Selector关闭,使用新建的Selector替换。
下面具体看下代码,首先检测是否发生了该BUG:
图2-27 epoll bug 检测
一旦检测发生该BUG,则重建Selector,代码如下:
图2-28 重建Selector
重建完成之后,替换老的Selector,代码如下:
图2-29 替换Selector
大量生产系统的运行表明,Netty的规避策略可以解决epoll bug 导致的IO线程CPU死循环问题。
netty的解决代码在package io.netty.channel.nio.nioEventLoop这个类下面。
文章来源:https://www.cnblogs.com/JAYIT/p/8241634.html
Selector空轮询相关推荐
- Netty : 臭名昭著的JDK的NIO bug(空轮询bug)
1.美图 2.概述 在搞这个问题的时候 Netty:Netty不断打开文件的BUG 一直找不到原因,然后偶然想了一下是不是netty的空轮询bug,后来查了一下真的是,第一感觉居然是,我草,我草,好兴 ...
- NIO的空轮询bug是什么?netty是如何解决NIO空轮询bug的?
文章目录 1. NIO的空轮询bug 2. netty如何解决NIO空轮询bug的? 1. NIO的空轮询bug JDK1.5开始引入了epoll基于事件响应机制来优化NIO.相较于select和po ...
- java为什么不解决空轮询,netty解决空轮询bug
selector在没有结果的情况下,依然被唤醒,导致一直空轮询,cpu100% 直接定位到NioEventLoop @Override protected void run() { for (;;) ...
- Netty : netty 4如何解决空轮询bug
1.美图 2.概述 空轮询bug参考:Netty : 臭名昭出的JDK的NIO bug(空轮询bug) 4.netty4 解决 4.1 构建阈值 int selectorAutoRebuildThre ...
- Netty : netty 3如何解决空轮询bug
1.美图 2.概述 空轮询bug参考:Netty : 臭名昭出的JDK的NIO bug(空轮询bug) 3. netty 3 如何解决 netty3采用的是第三种方案,检测重点是select函数是否返 ...
- java nio空轮循_Java nio 空轮询bug到底是什么
epoll机制是Linux下一种高效的IO复用方式,相较于select和poll机制来说.其高效的原因是将基于事件的fd放到内核中来完成,在内核中基于红黑树+链表数据结构来实现,链表存放有事件发生的f ...
- Netty框架之Selector轮询器
Selector的原理详解 1.传统多线程网络通信的服务器设计 2.线程池版网络通信的服务器设计 Selector版网络通信的服务器设计 说到Selector的作用,我们不得不引入 多线程网络通信的设 ...
- 再有人问你Netty是什么,就把这篇文章发给他
点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 本文转载于公众号:Hollis 本文基于Netty4.1展开介绍相关理论模型,使用场景,基本组件. ...
- 消息中间件—RocketMQ的RPC通信(二
作者:胡宗棠 来源:匠心独运的博客 在(一)篇中主要介绍了RocketMQ的协议格式,消息编解码,通信方式(同步/异步/单向).消息发送/接收以及异步回调的主要通信流程.而本篇将主要对RocketMQ ...
最新文章
- 【Harvest源码分析】获取F0轮廓
- python结果输出到文件-python print输出到文件
- POJ NOI MATH-7828 最大公约数与最小公倍数
- .NET Core 3.0 稳定版发布
- SQL 2005 Reporting Services:物理分页和逻辑分页 SSRS 2008 report export to PDF - Cannot get size to work...
- 千万级用户-亿级请求的平台架构演变
- 打印图形(内测第1届第1题)
- Atitit 信息系统安全法 目录 1. 常见的安全保护目标	1 2. WEB安全风险行为	2 2.1. Injection	2 2.2. Broker Authentication损坏的身份验证
- Object-c基础语法
- 一张图看懂财务报表分析
- 卸载精灵(bue directx) r4.0 完美版 绿色
- 深度 | 刘群:基于深度学习的自然语言处理,边界在哪里?
- 第二章 前端开发——JavaScript
- BAT面试经验分享(机器学习算法岗)
- 用于android的音乐可视化工具,Android音乐播放可视化
- 服务器usb电源型号,锂电池、龙威305D电源、故障平板、同轴电缆、惠普服务器电源、4T硬盘、USB显卡、AP等...
- 联想服务器双系统安装教程,联想笔记本装win8/win10双系统教程
- 【无标题】常见文件扩展名(后缀)及其对应文件类型和功能
- 快餐店促销活动流程,快餐店网络营销方案
- [深入研究4G/5G/6G专题-34]: URLLC-5-《中国联通5G URLLC技术白皮书3.0版本》解读-1-业务场景
热门文章
- Unity3d游戏引擎Windy系列教程:常见组件扫盲讲解引入unity所需的脚本语言基础
- 真杜比全景声家庭影院级投影设备,当贝做到了五千元内也支持
- sql查询电话号敏感数据加* 的写法
- iOS工程师 - 简历
- 什么样的打码网站算正规的打码网站
- 软件开发基于JavaScript实现快速转换文本语言(繁体中文和简体中文)_javascript技巧
- java实现消息队列以及延迟消息(队列DelayQueue)
- Java毕业设计-会议室预约小程序系统
- 网易考拉API,根据ID取产品详情 OneBound数据
- 基于java+ssm+mysql的大学生考勤管理系统及智能分析系统