事故現象:
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/