今天发现一台服务器上的springboot程序启动特别慢,完全启动起来用了有好几分钟。刚开始以为是代码写的有问题造成了卡死,直到看到这条log:
2017-03-08 10:06:49.600 INFO 6439 --- [main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8888 (http)
2017-03-08 10:06:49.613 INFO 6439 --- [main] o.apache.catalina.core.StandardService : Starting service Tomcat
2017-03-08 10:06:49.614 INFO 6439 --- [main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.0.37
......
2017-03-08 10:09:10.167 INFO 6439 --- [ost-startStop-1] o.a.c.util.SessionIdGeneratorBase : Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [140,108] milliseconds.
原来是tomcat的启动耗费了140多秒。而罪魁祸首是这个SecureRandom
类。
其实,这个问题我之前就有耳闻,但从没遇到过,也就没太在意。今天终于让我遇上了,墨菲定律
又生生应验了一回。。
tomcat的文档里有个概念叫Entropy Source(熵源)
Tomcat 7+ heavily relies on SecureRandom class to provide random values for its session ids and in other places. Depending on your JRE it can cause delays during startup if entropy source that is used to initialize SecureRandom is short of entropy. You will see warning in the logs when this happens, e.g.:
<DATE> org.apache.catalina.util.SessionIdGenerator createSecureRandom
INFO: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [5172] milliseconds.
意思是tomcat7以上的版本,在启动时会调用SecureRandom
类来生成随机数。如果用于初始化SecureRandom
的熵源
是个短熵
(熵不够用),那么就会报文章开头说的warning了。
jdk的配置文件中,使用securerandom.source
设置了熵源:
cat /usr/java/jdk1.8.0_121/jre/lib/security/java.security
securerandom.source=file:/dev/random
可以看到默认值是:/dev/random
。
所以程序启动后SecureRandom
类会读取/dev/random
以获取随机序列,这是一个同步操作。当熵池(entropy pool)
中没有足够的熵时,读取/dev/random
就会造成阻塞,直到收集到了足够的熵,程序才会继续往下进行。
(关于什么是/dev/random
,可以查看 wiki的介绍)
解决方法是修改成非阻塞的熵源/dev/urandom
。
可以修改java.security
文件中的securerandom.source
值,也可以使用参数java.security.egd
:
java -jar app.jar -Djava.security.egd=file:/dev/./urandom
至于为什么是/dev/./urandom
,而不是/dev/urandom
,这源于java的一个bug。大意是/dev/urandom
在某些情况下可能还是最终会转换成调用/dev/random
。所以为了保险起见,还是使用/dev/./urandom
吧!