Bug 记录:SecureRandom.getInstanceStrong()导致验证码获取阻塞
问题描述:
在发送验证码到邮件中,接口调用时卡在生成验证码阶段,导致验证码功能完全不可用;
经排查开发环境一切正常,测试环境会重现此问题;
问题分析:
现象:
- 代码卡在 SecureRandom.getInstanceStrong().nextInt() 无法继续执行
原因:
- SecureRandom.getInstanceStrong() 方法在 Linux 下默认使用/dev/random来生成随机数,而这个文件的数据来源于系统的扰动。当系统产生扰动很少的时候,就会导致读取这个文件的线程阻塞。
- /dev/random:阻塞型设备,当熵不足时会阻塞程序
- /dev/urandom:非阻塞型设备,熵不足时使用伪随机算法
熵池不足是什么意思?
- 熵池不足"是指系统中可用于生成随机数的随机性资源不足。当熵池中的熵值低于某个阈值时,系统无法提供足够的随机性
查询服务器熵值:
cat /proc/sys/kernel/random/entropy_avail
正常应该1000以上
解决方案:
方案一:使用使new SecureRandom()
new SecureRandom() 默认使用非阻塞源,在 Linux 环境下,Java 默认使用 /dev/urandom 作为熵源(永不阻塞,即使熵不足时使用伪随机算法补充)
方案二:使用ThreadLocalRandom来生成验证码
public class CaptchaNumCreator extends DefaultTextCreator {@Overridepublic String getText() {// 确保使用高效的随机数生成方式return ThreadLocalRandom.current().ints(6, 0, 10) // 生成6位数字.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString();}
}
方案三:在 JVM 启动参数中添加
-Djava.security.egd=file:/dev/./urandom
注意:这里使用 /dev/./urandom 而非 /dev/urandom 是为了绕过 JDK 的一个历史 Bug