Tomcat启动Creation of SecureRandom instance卡住解决

Tomcat启动因为 Creation of SecureRandom instance for session ID generation using [SHA1PRNG] 卡住

背景

起因是线上Tomcat的启动缓慢,看到日志里面有一行:
[2019-05-17 14:06:20] [localhost-startStop-1] INFO o.a.c.util.SessionIdGenerator [T:] Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [213,586] milliseconds.

原因

简单google了一下,是Java的SecureRandom是同步读的操作系统的/dev/random
(具体的这个配置是在JAVA_HOME/jre/lib/security/java.security这个文件里面的securerandom.source=file:/dev/random 这个配置项)
由于entropy pool(熵池)里面的值不够,所以卡住在读/dev/random上了,造成了启动缓慢

解决方法

1. 根据tomcat官方文档给出的方式(https://wiki.apache.org/tomcat/HowTo/FasterStartUp),在jvm启动的时候加入-Djava.security.egd=file:/dev/./urandom即可
2. 或者把JAVA_HOME/jre/lib/security/java.security里面的securerandom.source的值改为file:/dev/./urandom也可以

上述方法二选一即可。

./urandom是什么鬼

但是这里有个很奇怪的地方,为什么写法是file:/dev/./urandom,而不是file:/dev/urandom,我搜索了一下,换成file:/dev/urandom是不会生效的,读的还是file:/dev/random。这是jvm的一个bug,相关的记录

4705093
6202721

If java.security.egd or securerandom.source point to "file:/dev/random" or "file:/dev/urandom", we will use NativeSeedGenerator, which calls super() which calls SeedGenerator.URLSeedGenerator(/dev/random). 
(A nested class within SeedGenerator.)  The only things that changed in this bug was that urandom will also trigger use of this code path. 

其实最后都call到同一个SeedGenerator.URLSeedGenerator(/dev/random)上面去了
所以才有了这个workaround用file:/dev/./urandom这个格式。

但是这个问题在开发环境并不存在,为什么预发/线上会有?
因为我们的开发环境是redhat,线上是debian , 直接cat /dev/random就能发现开发环境能马上显示好几屏的随机数,而线上环境只有不到20个字符。具体/dev/random是啥见wikipedia:/dev/random.

这里摘取其中的一段

随机数发生器有一个容纳噪声数据的熵池,在读取时,/dev/random设备会返回小于熵池噪声总数的随机字节。
/dev/random可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random的读操作将会被阻塞,直到收集到了足够的环境噪声为止。
这样的设计使得/dev/random是真正的随机数发生器,提供了最大可能的随机数据熵,建议在需要生成高强度的密钥时使用。

/dev/random的一个副本是/dev/urandom(“unblocked”,非阻塞的随机数发生器),它会重复使用熵池中的数据以产生伪随机数据。
这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。
它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码。

/dev/random也允许写入,任何用户都可以向熵池中加入随机数据。
即使写入非随机数据亦是无害的,因为只有管理员可以调用ioctl以增加熵池大小。
Linux内核中当前熵的值和大小可以通过访问/proc/sys/kernel/random/文件夹中的文件得到