【Netty】Selector空轮询 解决 详解

shadowLogo

前言:

在之前的博文中,本人讲解了 Netty概念基本使用各种机制 以及 核心源码

那么,在本篇博文中,本人将来讲解一个 开发 中,很重要的问题 —— Selector空轮询 的解决


首先,本人来讲解下 什么是 Selector空轮询

概念:

Selector空轮询NIOAPIbug
NIO 中,使用 Selectorselect方法,来 轮询当前是否有IO事件
根据 JDK 中对 NIOapi描述

Selectorselect方法 会 一直阻塞,直到 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方法:

1

2
我们可以看到,解决逻辑 是:

  • 每次调用后,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) 的 核心逻辑
溜了溜了

posted @ 2021-05-08 18:56  在下右转,有何贵干  阅读(1198)  评论(1编辑  收藏  举报