最近启动Java服务,发现new SecureRandom()时间特别慢,于是研究了慢的原因,记录如下
慢的原因
在SecureRandom类的源码下,有一句描述This class provides a cryptographically strong random number
,也就是可以提供加密强
随机数。
SecureRandom的实现中,随机数的生成需要指定entropy source
,翻译作熵源。什么是熵?大家应该都听别人说过系统熵增,熵减,它用来描述一种无序的状态,是热力学的一个概念。随机数的生成需要的就是这种随机,无序
在Linux上(或者macos这种类unix系统),/dev/random
和 /dev/urandom
是最常见的熵源,是一个特殊的设备文件,可以用作随机数发生器或伪随机数发生器
/dev/random
依赖于熵池
提供的随机数据,什么是熵池
,摘录一段介绍:
The random number generator gathers environmental noise from device drivers and other sources into an entropy pool. The generator also keeps an estimate of the number of bits of noise in the entropy pool. From this entropy pool, random numbers are created.
熵池是Linux内核的一部分,是随机数获取的源头。
在对熵池的介绍里有这么一句话gathers environmental noise from device drivers and other sources
,也就是熵的收集依赖环境噪音,比如鼠标移动、键盘输入等。
每次读取/dev/random
熵源的时候,都会消耗熵池的数据,如果外部噪音(类似键盘敲击)没那么频繁,可能会有耗尽的时候,这时候就会block,也就是我们遇到的缓慢的原因
When the entropy pool is empty, reads from /dev/random will block until additional environmental noise is gathered
解决
对于上面提到的两个熵源,他们最显著的区别就是生成速度
/dev/random
:提供高质量的真随机数,依赖系统收集的环境噪声(比如鼠标移动、键盘输入等)作为熵源。当熵池(即系统收集的随机性)耗尽时,/dev/random 会停止提供随机数据,直到系统收集到足够的新熵。这意味着在熵不足的情况下,读取/dev/random会阻塞(等待)一段时间,适合需要高质量随机性的场景,如密钥生成。
/dev/urandom
:非阻塞版本的随机设备,也会使用熵池,但当熵池耗尽时不会停止提供随机数,而是会继续生成伪随机数。这种伪随机数是基于算法的,生成速度更快,因此不会因为熵耗尽而阻塞,适合大部分对随机性要求不太严格的场景,如一般的应用加密。
在 java.security 配置文件下,指定了使用何种熵源,默认是/dev/random
。服务里使用SecureRandom的目的仅为生成随机值用来延迟线程启动的时间,所以根本不需要/dev/random
,所以这里改为urandom即可
# 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
参考:
https://man7.org/linux/man-pages/man4/random.4.html