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/

http://hongjiang.info/java8-nativeprng-blocking/

https://blog.51cto.com/leo01/1795447

posted @ 2020-01-16 16:19  bookc  阅读(496)  评论(0编辑  收藏  举报