Netty「源码阅读」之怎么解决 Java 的 epoll 空轮询 bug

简介: Netty「源码阅读」之怎么解决 Java 的 epoll 空轮询 bug

前言

Java NIO中有一个著名的 bug epoll, 这个 bug 会导致Reactor线程被唤醒, 进行空轮询, 最终COU 100%爆满, 那么Netty是怎么解决epollbug 的呢

在上篇文章中我们有说过, NioEventLoop.run()方法是基于Selector的轮询方法, 在方法内部实现了死循环去获取网络IO事件并执行

也是在该方法中Netty巧妙地解决了epollbug, 接下来让我们定位到该方法


Netty 解决 epoll

在这个方法中, 我将重点的步骤进行了标注, 先说流程, 然后在进行针对性的讲解

  • 初始化selectCnt 这个变量是解决epoll的关键
  • 定义变量strategy, 他是int类型, 默认是0
  • 执行selectStrategy.calculateStrategy()方法获取当前网络IO事件数量
  • 根据strategy来执行不同的操作, 这个后面贴详细代码讲一下
  • 这个时候我们的变量selectCnt++
  • 我们会对当前执行的任务和strategy进行判断, 对selectCnt进行归零操作
  • 否则, 我们会对执行方法unexpectedSelectorWakeup()selectCnt进行边界值判断

网络异常,图片无法展示
|

Switch

switch中, 一共case了三个常量

  • SelectStrategy.CONTINUE: 继续, 直接跳过, 这里不做讲解
  • SelectStrategy.BUSY_WAIT: NIO不支持 BUSY_WIAT, 所以和 SELECT 是一样的
  • SelectStrategy.SELECT: 判断当前 IO事件 是否就绪, 如果没有就绪就让线程休息, 不进行唤醒

网络异常,图片无法展示
|

SelectStrategy.SELECT

具体判断线程是否要苏醒的地方是下面两个红框

网络异常,图片无法展示
|

unexpectedSelectorWakeup()方法

实际上解决空轮询问题的地方就是在这里

网络异常,图片无法展示
|

在这个方法中我们可以看到, 方法返回值是判断selectCnt是否大于静态变量SELECTOR_AUTO_REBUILD_THRESHOLD, 那么我们看一下这个变量的赋值过程

网络异常,图片无法展示
|

跟着序号看起, 先是定义了变量, 然后将selectorAutoRebuildThreshold赋值于SELECTOR_AUTO_REBUILD_THRESHOLD, 可以看到执行了SystemPropertyUtil.getInt方法, 这个方法是获取你配置文件中的值, 如果你的配置文件中没有进行配置, 那么就使用后面的默认值512

这也就是为什么, 我们经常看到说: 当Netty空轮询阈值是512

假设我们的selectCnt已经达到了512, 还会执行一个方法rebuildSelector();顾名思义, 重建Selector

网络异常,图片无法展示
|

这个方法的作用是将当前这个发生空轮训的selector上的selectedKeys挪到新建的selector选择器上,从而完美解决空轮训的问题

然后我们的unexpectedSelectorWakeup返回true, 回到之前的run()方法中可以看到, 作者将selectCnt重新赋值为了0, 所以作者也不算是解决了这个bug, 但是巧妙的将其绕过了

网络异常,图片无法展示
|

总结

总结就简单写写, 初始化变量selectCnt, 每次轮询进行++操作, 后面会将其进行归零, 如果没有归零selectCnt, 会执行unexpectedSelectorWakeup()方法, 如果达到了512, 会进行selector重建



目录
相关文章
|
分布式计算 DataWorks Java
DataWorks操作报错合集之在使用MaxCompute的Java SDK创建函数时,出现找不到文件资源的情况,是BUG吗
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
280 0
|
存储 Java
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
前言 下面,跟上主播的节奏,马上开始ThreadLocal源码的阅读( ̄▽ ̄)" 内部结构 如下图所示,我们可以知道,每个线程,都有自己的threadLocals字段,指向ThreadLocalMap
759 81
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
|
Java
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
因为本文说的是ReentrantLock源码,因此会默认,大家对AQS有基本的了解(比如同步队列、条件队列大概> 长啥样?)。 不懂AQS的小朋友们,你们好呀!也欢迎先看看这篇
346 13
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
|
算法 Java 容器
Netty源码—4.客户端接入流程
本文主要介绍了关于Netty客户端连接接入问题整理、Reactor线程模型和服务端启动流程、Netty新连接接入的整体处理逻辑、新连接接入之检测新连接、新连接接入之创建NioSocketChannel、新连接接入之绑定NioEventLoop线程、新连接接入之注册Selector和注册读事件、注册Reactor线程总结、新连接接入总结
|
安全 Java 调度
Netty源码—3.Reactor线程模型二
本文主要介绍了NioEventLoop的执行总体框架、Reactor线程执行一次事件轮询、Reactor线程处理产生IO事件的Channel、Reactor线程处理任务队列之添加任务、Reactor线程处理任务队列之执行任务、NioEventLoop总结。
|
安全 Java
Netty源码—2.Reactor线程模型一
本文主要介绍了关于NioEventLoop的问题整理、理解Reactor线程模型主要分三部分、NioEventLoop的创建和NioEventLoop的启动。
|
编解码 安全 Java
Netty源码—1.服务端启动流程
本文主要介绍了服务端启动整体流程及关键方法、服务端启动的核心步骤、创建服务端Channel的源码、初始化服务端Channel的源码、注册服务端Channel的源码、绑定服务端端口的源码、服务端启动流程源码总结。
|
小程序 Java 关系型数据库
基于Java微信小程序小说阅读系统设计和实现(源码+LW+调试文档+讲解等)
基于Java微信小程序小说阅读系统设计和实现(源码+LW+调试文档+讲解等)
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
683 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码