引子
使用springboot開發一個部署在阿里雲上的項目的時候,用controller+freemarker實現了一個偽js,用於靜態頁面同步獲取服務器上下文、當前登錄用戶等信息使用,結果發現因為這個偽js導致頁面第一次加載十分緩慢。
跟蹤后發現不僅是該偽js,是對所有的controller的第一次訪問會非常的慢,之后的短時間內又會很正常,仔細跟蹤日志發現訪問慢的那一次請求中會出現類似如下這樣的一段或多段日志:
2017-10-17 14:12:03.971 [http-nio-8086-exec-2] INFO o.a.c.u.SessionIdGeneratorBase.log<179>:
Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [61,115] milliseconds.
重點就在這個時間,重復測試發現這個時間甚至可以長至5~6分鍾,最短也要1分多鍾,
以“SecureRandom”等關鍵字求助搜索引擎,終於找到了巨慢的罪魁禍首。
原因:
StackOverflow上Dan Dyer的回答原文:
On Linux, the default implementation for
SecureRandom
isNativePRNG
(source code here), which tends to be very slow. On Windows, the default isSHA1PRNG
, which as others pointed out you can also use on Linux if you specify it explicitly.
NativePRNG
differs fromSHA1PRNG
and Uncommons Maths' AESCounterRNG in that it continuously receives entropy from the operating system (by reading from/dev/urandom
). The other PRNGs do not acquire any additional entropy after seeding.
AESCounterRNG
is about 10x faster thanSHA1PRNG
, which IIRC is itself two or three times faster thanNativePRNG
.If you need a faster PRNG that acquires entropy after initialization, see if you can find a Java implementation of Fortuna. The core PRNG of a Fortuna implementation is identical to that used by AESCounterRNG, but there is also a sophisticated system of entropy pooling and automatic reseeding.
大意如下:
linux默認的
SecureRandom
實現方式是非常慢的NativePRNG
,而在Windows上的默認實現是SHA1PRNG
,正如其他人所說,你也可以在Linux上指定使用該方式。……
NativePRNG
會不斷從操作系統獲取熵(從/dev/urandom
讀取),其它PRNG則不會獲取這些額外的熵。
AESCounterRNG
差不多比SHA1PRNG
快的十倍,而如果我沒記錯,SHA1PRNG
比NativePRNG
快兩到三倍
結合其它資料,終於弄明白了,SecureRandom是java用來獲取高級隨機數的實現,++而巨慢則是由在linux上默認使用的實現方式導致的++。
解決:
Linux上Jre中默認SecureRandom讀取熵時會從/dev/urandom
中讀取,而從這里讀取熵是阻塞的,下面是已知的兩種解決方式。
方法一,修改jre配置文件(該方式本人實測成功):
以linux用yum安裝的jre為例,打開
/usr/lib/jvm/jre/lib/security/java.security
,找到
securerandom.source=file:/dev/random
這一行,將
file:/dev/random
(阻塞熵源)修改為file:/dev/./urandom
(非阻塞熵源)
方法二,為JVM增加如下啟動參數(該方式本人未親自測試):
-Djava.security.egd=file:/dev/./urandom
Thanks To: How to solve slow Java SecureRandom
? - StackOverflow
另附本項目環境如下:
Linux version 3.10.0-514.26.2.el7.x86_64 (builder@kbuilder.dev.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) ) #1 SMP Tue Jul 4 15:04:05 UTC 201
java-1.8.0-openjdk-1.8.0.141-1.b16.el7_3.x86_64 jre jre-1.8.0 jre-1.8.0-openjdk jre-1.8.0-openjdk-1.8.0.141-1.b16.el7_3.x86_64 jre-openjdk
springboot-1.5.4.RELEASE
據同事測試,同樣的jre、springboot在他的centos虛擬機上並未出現該問題,所以推測有可能是linux版本差異,或是安全策略配置問題,亦或是linux存在這么一個可以配置默認隨機數獲取算法的方式,有待深究。