【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
) 的 核心逻辑