Tomcat 生成 Session 堵塞引发的思考
事故现象:
public void handle(HttpServletRequest request, HttpServletResponse response) { //运行到此处的时候会阻塞,然后查看日志就爆出了如下日志 Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [14,387] milliseconds. if (request.getSession().getAttribute(WebConstants.SESSION.SESSION_USER_INFO) != null) { //... } //... }
原因是 getSession() 时候 需要生成 SessionId,在生成Id的时候需要生成随机数,此时由于采用生成随机数的策略(file:/dev/random)是堵塞的,所以会堵塞很久,解决方案是采用非堵塞(file:/dev/urandom)方式生成随机数。
好了,现在就出现了网上大多数的解决方式,启动参数添加如下参数,具体的原因可参考最下面几个连接
-Djava.security.egd=file:/dev/./urandom
但是,重点来了,如果你的JDK版本是 JDK8,其实启动参数添加以下即可,上下的区别在于多了一个 “./”(PS:其实在JDK8中 添加了 “./” 也能避免堵塞,但是是以另一种方式避免的,这个在文章最后会提到)
-Djava.security.egd=file:/dev/urandom
为何在JDK8 中 不需要添加 “./” ?
请看 SeedGenerator.java 代码,只要你设置了 java.security.egd ,那么就会使用你所指定的生成器,而 JDK7 则不同,请继续往下看
JDK7的代码如下
如果 java.security.egd 参数指定的是 file:/dev/random 或者 file:/dev/urandom,则调用了无参的NativeSeedGenerator构造函数,而无参的构造函数将默认使用 file:/dev/random ,所以在JDK7中如果想要使用 file:/dev/urandom 就必须绕开第一个 if 判断,而使用 file:/dev/./urandom ,这样子才能使用你自定义的生成器
那么,问题又来了,为什么在JDK8中使用 -Djava.security.egd=file:/dev/./urandom 也能解决堵塞的问题呢?
所以,虽然 JDK8 使用 -Djava.security.egd=file:/dev/./urandom 也能达到相同的效果,但是它并不是使用我们给它解决方案来解决问题的
附录:
以下是 getSession() 生成随机数的关键断点处,有兴趣的话,可以动手断点运行一下
参考:
https://blog.csdn.net/u011687186/article/details/73224733
http://hongjiang.info/jvm-random-and-entropy-source/