【Netty】Selector空轮询 解决 详解

前言:
在之前的博文中,本人讲解了 Netty 的 概念、基本使用、各种机制 以及 核心源码
那么,在本篇博文中,本人将来讲解一个 开发 中,很重要的问题 —— Selector空轮询 的解决
首先,本人来讲解下 什么是 Selector空轮询:
概念:
Selector空轮询是NIO的 API 的 bug
在NIO中,使用Selector的 select方法,来 轮询当前是否有IO事件,
根据 JDK 中对NIO的 api描述
Selector的 select方法 会 一直阻塞,直到 IO事件达到 或 超时但是在某些场景下,select方法 会 直接返回
即使 没有超时,并且也没有IO事件到达,也会 直接返回
这就是 Selector空轮询(epoll bug)
一般发生了 Selector空轮询,就会 一直 空轮询,陷入死循环,因此 CPU飙到100%
最终,线上问题频频,服务器崩溃!
可怕的是:到目前为止,JDK还是无法完全避免这个bug产生!
因此,所有使用 NIO 的 成熟框架,都会对 这个bug 做出一些解决
现在,本人来讲解下,在 Netty 中,Selector空轮询(epoll bug) 是如何解决的:
Netty 中的 解决方案:
在 Netty 中,Selector空轮询(epoll bug) 的解决,是在 select方法 中的:
Selector空轮询 的解决 —— select方法:


我们可以看到,解决逻辑 是:
- 每次调用后,selectCnt + 1
- 若 超时(正常 跳出阻塞),
重置selectCnt的值- 若 未超时(非正常 跳出阻塞),
重新构造一个selector,并 重置selectCnt的值
那么,本人再来讲解下,Netty 是如何 重新构造 Selector 的:
重新构造 Selector —— selectRebuildSelector方法:

我们可以看到:
内部只是:
- 重新构造 selector
- 使用 新的selector,进行 阻塞式监听
那么,我们来看看,rebuildSelector方法 是如何实现的:
rebuildSelector方法:

我们可以看到:
内部只是:
- 调用 rebuildSelector0方法,也就是 真正的 重建Selector逻辑
真正的 重建Selector逻辑 —— rebuildSelector0方法:



我们可以看到:
内部只是:
- 新建一个 Selector
- 将 旧的Selector 上注册的key,全部 注册 到 新的Selector 上
- 将 当前Selector 指向 新的Selector
- 关闭 旧的Selector
那么,最后,本人来 总结 下 在 Netty 中,Selector空轮询(epoll bug) 是怎样解决的:
总结:
解决逻辑 为:
- 每次调用后,selectCnt + 1
- 若 超时(正常 跳出阻塞),
重置selectCnt的值- 若 未超时(非正常 跳出阻塞),
重新构造一个selector:
- 新建一个 Selector
- 将 旧的Selector 上注册的key,全部 注册 到 新的Selector 上
- 关闭 旧的Selector
- 使用 新的selector,进行 阻塞式监听
,并 重置selectCnt的值
最后,可能同学们会有疑问:
selectCnt 在 rebuild selector时 没用啊,为什么要维护那个变量呢?
答曰:
系统日志输出,方便开发人员进行分析优化处理
以上,就是 Netty 解决 Selector空轮询(epoll bug) 的 核心逻辑


浙公网安备 33010602011771号