发布网友 发布时间:2022-11-16 12:47
共1个回答
热心网友 时间:2024-12-15 07:20
本文主要在 应用服务器中对JDK的epoll空转bug的处理 基础上做了修补。
JDK bug列表中有两个相关的bug报告:
JDK-6403933的bug说出了实质的原因:
具体解释为:在部分Linux的2.6的kernel中,poll和epoll对于突然中断的连接socket会对返回的eventSet事件集合置为POLLHUP,也可能是POLLERR,eventSet事件集合发生了变化,这就可能导致Selector会被唤醒。
这是与操作系统机制有关系的,JDK虽然仅仅是一个兼容各个操作系统平台的软件,但很遗憾在JDK5和JDK6最初的版本中(严格意义上来将,JDK部分版本都是),这个问题并没有解决,而将这个帽子抛给了操作系统方,这也就是这个bug最终一直到2013年才最终修复的原因,最终影响力太广。
grizzly的commiteer们最先进行修改的,并且通过众多的测试说明这种修改方式大大降低了JDK NIO的问题。
但是,这种修改仍然不是可靠的,一共有两点:
最终的终极办法是创建一个新的Selector:
Jetty首先定义两了-D参数:
第一个参数是select返回值为0的计数,第二个是多长时间,整体意思就是控制在多长时间内,如果Selector.select不断返回0,说明进入了JVM的bug的模式。
做法是:
思路和Jetty的处理方式几乎是一样的,就是netty讲重建Selector的过程抽取成了一个方法。
netty 会在每次进行 selector.select(timeoutMillis) 之前记录一下开始时间currentTimeNanos,在select之后记录一下结束时间,判断select操作是否至少持续了timeoutMillis秒(这里将time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos改成time - currentTimeNanos >= TimeUnit.MILLISECONDS.toNanos(timeoutMillis)或许更好理解一些),
如果持续的时间大于等于timeoutMillis,说明就是一次有效的轮询,重置selectCnt标志,否则,表明该阻塞方法并没有阻塞这么长时间,可能触发了jdk的空轮询bug,当空轮询的次数超过一个阀值的时候,默认是512,就开始重建selector