管你 JDK 还是 Linux,我 Netty 稳坐钓鱼台

你好,我是yes。

今天聊的也是一个老生常谈的问题了:JDK Selector 的空轮询 bug。

今天来简单的扒一下,这玩意大概能追溯到 06 年,并且基于这个 BUG 我们再发散下,看看能给我们什么启发。

追溯

最近我不是一直在写 Netty 系列嘛,我想谈到 Netty ,但凡你在网上看过相关资料,那肯定会提到 JDK NIO 在 Linux 系统下空轮询的 bug,就是调用 Selector.select(timeout),即使没事件发生,也不会阻塞 timeout 时间,而是立马 return,这样的空轮询导致 CPU 100%。

产生这个 bug 大致的原因我讲下:连接突然中断,poll 和 epoll 会被 POLLHUP 或者 POLLERR 事件唤醒,于是 Selector 就被唤醒了,但是 JDK Selector 一看,没事件(JDK只定义了CONNECT、READ、WRITE、ACCEPT这几个事件)需要处理啊?

然后就又循环了…没事件要处理啊,然后就又循环了…没事件要处理啊,然后就又循环了…

如此往复,空轮询使得 CPU 100%。

这个 BUG 真算是个老黄历了,我查了查 JDK 的 bug 库,大致 13 年 3 月份之后就没提相关的 bug 了,而且按照官方的说法这也和 Linux 版本有关,至今应该没这问题了?(我不确定)。

我们来回顾一下 bug 库的历史。

我查了下最早提到 Selector(底层用的是 poll 或者 epoll) 在 Linux 不会阻塞的 BUG 是在 06 年 3 月 24 日。

可以看到,这 Resolved 的日期有点长,隔了一年,也就是 06 提的 07 年才说修复了,不过当天是给了个解决方案的:


解决方案简单粗暴,就是把抽风不阻塞的 Selector 删了,然后创建个新的,取而代之。(有时候简单粗暴的就是最好的)

我再往后找了找 Selector 的 bug ,发现 08 年还有 bug(不是说修复了吗?),并且处理的日期是 13 年!最终结果是无法修复,相关的是 JDK 6 版本。


同 13 年还有类似的 bug ,不过是在 JDK 7 版本上,最终 resolution 是不完整的修复!

从这个处理时间和结果来看,我个人推断 JDK 对此 bug 的态度是消极的,认为这是 Linux 自身的 bug 导致的现象(同为程序员习惯性地甩)。

JDK-6670302 的评价就可以看出:

大致的意思就是:升级下 Linux 内核版本(2.4版本有这个问题)就好了,2.6 版本发布已经 4 年了都,这个修复也没啥必要了,需求很小。

这其实可以理解。

站在 JDK 开发者的角度来看:我这代码在 windows 下运行的好好的,到你 Linux 就不行了?嗯?我的问题?Linux 为什么给我这样莫名其妙的事件?你中断唤醒了个什么玩意?

但是站 Linux 开发者角度来看就不一样了:嗯?甩锅给我?明明是你写代码没考虑到这种特殊情况,搁着跟我甩锅呢?

站我们 Java 开发而言:你搁着跟我搁那呢?有 bug 还搁这甩,我管你 JDK 什么情况,你就得给我修!(我相信 JDK 开发者也是这样看 Linux 开发者的)

哈哈哈,真不真实?

总之,我个人觉得这个 bug 之所以会被网上的文章拿出来反复鞭尸,一是因为 netty 通过曲线救国的方式,确实避免了那个时间段部署在 Linux 的应用直接用 Java Selector 产生的空轮询 bug,所以在当时谈到 Netty 就值得拿这个说事儿。

二是,天下文章一大抄嘛,懂的都懂。

对了,虽然我查 bug 库发现后面确实都没再有类似的bug,但是网上有文章说在 JDK8 还是重现了这个 bug!

链接:https://juejin.cn/post/6844903491505242119

啧啧,俗话说得好,靠人不如靠己,Netty 就是靠己来解决这个 bug,也就是上面提到的简单粗暴的解决办法!

Netty :空循环是吧,我数数你循环几次,只要到达一定次数,我就认为你产生 bug 了,于是我就重建一个 Selector,废弃以前那个已经抽风了的 Selector!这样我管你 JDK 还是 Linux 会不会处理,我这波就是稳坐钓鱼台!

这就是 Netty 的解决办法~所以也不能说是 Netty 修复了 JDK NIO 的 bug ,它只是曲线救国,变相避免了这个 bug 罢了。

这其实能给我们日常的开发提供一点思路,有时候求人不如求己,对于二方、三方的接口还是报以质疑的态度去看待,不要过度的相信他们,特别是三方的接口,一定要做好对方挂掉或者返回奇怪结果的准备。

我之前对接某大厂的接口,返回值就无声无息的变了,没有任何公告和通知,就是那种你认为不可能会变的值。比如一个返回城市名的接口,正常返回叫杭州市,它莫名其妙变了个杭州市(常用)。当然我只是举个例子哈,具体是啥不方便说。

还有之前对接过另一个大厂的接口,那时候他们的服务几乎属于要挂的情况,返回的贼慢,经常超时,这种我还是有经验的,起初就我设置了接口超时等待响应时间是 3s,而对方服务有问题,往往都超过了 3s,然后向对方提了工单,对方竟然让我把超时等待时间调整到 10s,我听得都傻了,正常返回100ms的接口,让我设个 10s,这是让我跟着他的服务一起挂是吗。

遇到这种情况可千万别听对方的,你得想到这就是在拖垮你的应用,你设置的超时等待的时间越久,线程被占用时间就的越长,那其他被的请求不就没线程去处理了嘛,然后请求就堆积了,最终你的应用就全部崩盘了。

也亏对方想得出来这种回复,遇到这种类似的情况,如果你个人拿捏不准,及时找你同事或者领导讨论下,别傻傻的听他的就改了。

总而言之,对待二方、三方接口,要多加个心眼,一定要做好判空、降级等等情况。我看有些新同学就很不喜欢判空,因为觉得多写一个 if 很丑陋,啧啧,年轻还是太年轻了,没遭受过毒打!

所以,你们遇到三方最恶心的场景是什么?拿到留言区给大家乐呵乐呵?

好了,今天就扯到这儿~

我是yes,从一点点到亿点点,我们下篇见~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值