0%

随机数生成与熵池策略

Preface

最近同事在将jar包上传到服务器执行spark-submit时,运行的时间总是不确定的,有时甚至等待数十分钟。遂想到当初自己在服务器上运行Tomcat时也遇到这种问题,往往第一次运行很快,后面要等很长时间。最近查阅相关资料,终于发现问题所在。

Concepts

首先要明白操作系统如何产生随机数以及内核熵池的一些概念

  • 随机数在许多领域都有重要应用,如密码学、网络安全等。Linux内核从1.3.30版本开始实现了一个高强度的随机数产生器

  • Linux 内核采用来描述数据的随机性。熵是描述系统混乱无序程度的物理量,一个系统的熵越大则说明该系统的有序性越差,即不确定性越大。在信息学中,熵被用来表征一个符号或系统的不确定性,熵越大,表明系统所含有用信息量越少,不确定度越大

  • 计算机本身是可预测的系统,因此,用计算机算法不可能产生真正的随机数。要生成高强度的随机数需要许多不确定的外界参数。你比如说编程中的随机数种子就是一个外界参数,如果你的种子数确定,那么生成的随机序列是可以预测的

  • 恰好机器的环境中充满了各种各样的噪声,如硬件设备发生中断的时间,用户点击鼠标的时间间隔等是完全随机的,事先无法预测。Linux内核实现的随机数产生器正是利用系统中的这些随机噪声来产生高质量随机数序列

  • 内核维护了一个熵池用来收集来自设备驱动程序和其它来源的环境噪音。为跟踪熵池中数据的随机性,内核在将数据加入池的时候将估算数据的随机性,这个过程称作熵估算。熵估算值描述池中包含的随机数位数,其值越大表示池中数据的随机性越好

  • 内核提供了两个字符设备/dev/random/dev/urandom以让用户调用获得随机数序列,/dev/random输出的随机数序列质量更高,适合于高强度的加密算法,但它只返回熵估算值所允许的最长的随机数序列,否则请求将被阻塞直到熵估算值增大到一定域值。/dev/urandom总是立即返回所请求的随机数序列,”u”代表unlocked非阻塞

  • 当熵池为空时,来自/dev/random的读操作将被阻塞,直到熵池收集到足够的环境噪声数据。这么做的目的是生成尽可能随机的数据。对于生成高质量的加密密钥或者是需要长期保护的场景,一定要这么做。

Ok,其实到这里,原因就已经明了了,在启动Tomcat生成SessionID时和spark-submit连接数据库时,用到了 /dev/random生成的随机数,却因为熵池用空而进入阻塞,这也解释了为什么第一次的执行很快,后面却很慢。

Solving

Apache官网上也有描述如何优化Tomcat的运行效率,有两种方法解决这个问题

  • 修改${java_home}/jre/lib/security/java.security中的这一行
    1
    securerandom.source=file:/dev/random
    替换为
    1
    securerandom.source=file:/dev/./urandom
  • 或者在Tomcat的catalina.sh中加入如下行
    1
    -Djava.security.egd=file:/dev/./urandom

Tips

  • 关于为什么加/./有人说是Bug,也有人说是故意这么设计的,oracle的官方文档 Java Bug Database

Reference