Tomcat启动卡死是因SecureRandom使用/dev/random阻塞导致,表现为日志中“Creation of SecureRandom instance…took [X] milliseconds”耗时过长;解决方法是将securerandom.source改为file:/dev/./urandom(JVM级更稳妥)或添加-Djava.security.egd=file:/dev/./urandom启动参数。

怎么看是不是SecureRandom卡住
直接看Tomcat启动日志里有没有类似这样的行:[localhost-startStop-1] INFO o.a.c.util.SessionIdGenerator Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [213586] milliseconds。数字越大(比如超过几万毫秒),越可能是这个问题。另外,卡住时线程堆栈里常出现 java.security.SecureRandom.generateSeed 或 NativeSeedGenerator,用 jstack <pid> 抓一下就能确认。
为什么改 /dev/urandom 不生效,非得写 /dev/./urandom
这是JVM的一个已知行为缺陷:只要 securerandom.source 或 java.security.egd 的值以 file:/dev/random 或 file:/dev/urandom 开头,JVM内部就会 fallback 到 URLSeedGenerator(/dev/random),仍然走阻塞路径。
加个 ./ 是绕过这个逻辑的 trick,让字符串不精确匹配,从而触发非阻塞的 NativeSeedGenerator 分支。所以必须写成:
securerandom.source=file:/dev/./urandom- 或启动参数:
-Djava.security.egd=file:/dev/./urandom
写成 /dev/urandom 或 /dev/.urandom 都不行 —— 前者被识别为“明确指定 urandom”,后者根本不是合法路径。
改哪里更稳妥:JVM级还是Tomcat级
两个地方都能生效,但优先改 JVM 级配置($JAVA_HOME/jre/lib/security/java.security),因为一劳永逸,所有 Java 进程都受益;而只改 Tomcat 的 catalina.sh 只影响当前实例。
实操建议:
- 先备份原文件:
cp $JAVA_HOME/jre/lib/security/java.security{,.bak} - 用
sed -i 's#securerandom\.source=.*#securerandom.source=file:/dev/./urandom#' $JAVA_HOME/jre/lib/security/java.security - 确认修改后重启 Tomcat,再检查日志是否还有超长 SecureRandom 初始化耗时
如果不能动全局 JVM 配置(比如多租户环境),就改 catalina.sh,在 JAVA_OPTS 里追加:-Djava.security.egd=file:/dev/./urandom。
熵池低的服务器上还有哪些坑要注意
这个问题在容器、云主机、最小化安装的 Linux(如 Debian netinst、Alpine)上特别常见,因为缺乏键盘/鼠标/磁盘中断等熵源。除了 SecureRandom 卡住,还可能影响:
- OpenSSL 初始化(比如某些 HTTPS client 启动慢)
- Java
KeyPairGenerator生成 RSA 密钥对极慢 - Spring Boot 的
spring.session.cookie.http-only相关初始化延迟
临时缓解可装 haveged 或 rng-tools 补充熵池,但最轻量、最通用的解法仍是强制用 /dev/./urandom —— 它不解决熵低本身,但绕过了阻塞等待,对 session ID 这类场景完全够用。


















