你好,我是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,从一点点到亿点点,我们下篇见~