JDK NIO
的epoll bug
,会导致Selector
空轮询,最终导致CPU 100%
Bug
出现的原因
若Selector
的轮询结果一直为空,没有新消息处理,则发生空轮询,CPU使用率100%(在selector
中,即使是select
轮询事件为0的话,照样不断的从select
本应该阻塞状态下wake up
出来)
Netty
的解决方案
- 对
Selector
的select
操作周期进行统计,每完成一次空的select
操作进行一次计数 - 若在某个周期内连续发生N次空轮询,则触发了
epoll
死循环bug。 - 重建
Selector
,将出现bug的Selector
上的channel
重新注册到新的Selector
上,并将原来的Selector
关闭,使用新的Selector
进行替换。
JDK
源码分析:
JDK NIO
执行代码如下:
执行selector.select()
方法返回keys是0,所以本应该对key值进行遍历的事件处理根本没有执行,又回到最上面的while(true)
循环,循环往复,不断的轮询,直到系统出现100%的CPU情况,最终导致程序崩溃。
Netty
源码分析
具体位置在实现类NioEventLoop
中:
netty
会在每次进行 selector.select(timeoutMillis)
之前记录一下开始时间currentTimeNanos
,在select
之后记录一下结束时间,判断select
操作是否至少持续了timeoutMillis
时间;selectCnt
记录空轮询次数
重建selector
当发生epoll bug
,则创建一个新的Selector
,将出现bug的Selector
上的channel
重新注册到新的Selector
上,关闭bug的Selector
,使用新的Selector
进行替换 :
Netty
的解决策略:
- 根据该
bug
的特征,首先侦测该bug
是否发生 - 将问题
Selector
上注册的Channel
转移到新建的Selector
上 - 老的问题
Selector
关闭,使用新建的Selector
替换