近期將一個老項目向ARM版的CentOS7移植時,遇到了SpringBoot啓動順利,但訪問頁面卡住的問題。由於是aarch64架構,因此使用了openjdk,這個項目之前在x86_64環境下一直是用Oracle的ServerJRE,沒有遇到問題。此次啓動正常,但啓動完成後,訪問部分頁面正常,部分頁面會卡住,卡住的時間不固定,有時長有時短,毫無規律可言。而且當卡住的頁面正常後,再刷新不會再次卡住。
第一想法肯定是查日誌,在首次訪問卡頓頁面時,Spring框架有一條這樣的WARN:
2019-12-09 17:40:32.995 WARN 15161 --- [https-jsse-nio-443-exec-8] o.a.c.util.SessionIdGeneratorBase : Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [178,241] milliseconds.
爲Session創建SecureRandom實例耗時將近3分鐘,這就是頁面卡住的原因,同時也解釋了爲什麼只有部分頁面卡住,因爲不是所有頁面都使用了Session,同時也解析了爲什麼卡住的頁面可訪問後再刷新就正常了,因爲創建SecureRandom instance只進行一次。
翻回來看看原因,項目中使用了Tomcat Embed作爲內嵌WEB服務器,而Tomcat在生成session ID時會使用org.apache.catalina.util.SessionIdGeneratorBase來產生安全隨機類SecureRandom實例。爲了算法保密性較強,需要用到僞隨機數生成器,Tomcat用到的是SHA1PRNG算法,爲了得到隨機種子,在Linux中,一般從/dev/random或/dev/urandom中產生,兩者原理都是利用系統的環境噪聲產生一定數量的隨機比特,區別在於系統環境噪聲不夠時,random會阻塞,而urandom會犧牲安全性避免阻塞。
從卡頓現象上看,一定是用了/dev/random導致的,看一下$JAVA_HOME/jre/lib/security/java.security文件,找到下面的內容:
# # Sun Provider SecureRandom seed source. # # Select the primary source of seed data for the "SHA1PRNG" and # "NativePRNG" SecureRandom implementations in the "Sun" provider. # (Other SecureRandom implementations might also use this property.) # # On Unix-like systems (for example, Solaris/Linux/MacOS), the # "NativePRNG" and "SHA1PRNG" implementations obtains seed data from # special device files such as file:/dev/random. # # On Windows systems, specifying the URLs "file:/dev/random" or # "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding # mechanism for SHA1PRNG. # # By default, an attempt is made to use the entropy gathering device # specified by the "securerandom.source" Security property. If an # exception occurs while accessing the specified URL: # # SHA1PRNG: # the traditional system/thread activity algorithm will be used. # # NativePRNG: # a default value of /dev/random will be used. If neither # are available, the implementation will be disabled. # "file" is the only currently supported protocol type. # # The entropy gathering device can also be specified with the System # property "java.security.egd". For example: # # % java -Djava.security.egd=file:/dev/random MainClass # # Specifying this System property will override the # "securerandom.source" Security property. # # In addition, if "file:/dev/random" or "file:/dev/urandom" is # specified, the "NativePRNG" implementation will be more preferred than # SHA1PRNG in the Sun provider. # securerandom.source=file:/dev/random
果然用的是/dev/random,按照上面的註釋部分,解決方案也不復雜,可以添加啓動參數或者修改java.security:
解決方法1:
啓動參數添加-Djava.security.egd=file:/dev/urandom,如:
java -Djava.security.egd=file:/dev/urandom -jar xxxxx.jar
解決方法2:
修改$JAVA_HOME/jre/lib/security/java.security,找到securerandom.source並修改:
securerandom.source=file:/dev/urandom
再重啓站點,卡頓現象消失。