SecureRandom随机数引起的故障

发布时间 2023-11-06 21:28:30作者: 爱吃橘子的胖达

故障现象:

接口大面积超时(数十秒到十多分钟不等)、接口大面积报错(比如连接池报错);
常见监控如 JVM、数据库连接、SQL 查询、网络、请求量都没有异常。

问题分析:

问题开始于修复 Sonar 问题

public class MathUtils {

    /**
     * 根据长度,生成指定位数的随机数
     */
    public static String genRandomNumber(Long len){
        Random random = new Random();//这里会被Soanr检测出代码异味
        StringBuilder result = new StringBuilder();
        for(int i=0;i<len;i++){
            result.append(random.nextInt(10));
        }
        return result.toString();
    }
}

Sonar 给出的建议如下:
Alt text
建议中提到,Random 不应该作为局部变量,否则每次生成随机数都会 new 一个 Random 变量。这部分建议是没问题的,问题在于下面的代码建议。

// SecureRandom is preferred to Random
private Random rand = SecureRandom.getInstanceStrong();  
public void doSomethingCommon() {
  int rValue = this.rand.nextInt();
  //...

代码中建议使用 SecureRandom 来替代 Random。SecureRandom 对象是把双刃剑,一方面,SecureRandom 被称为“真随机数”,适合对随机数要求特别高的场景;另一方面,SecureRandom 又可能引发性能问题,下面简单介绍一下来龙去脉。
首先,Java 的 Random 类是有一些缺陷的,参见 初看一脸懵逼,看懂直接跪下! - 掘金,不适合对随机数要求高的场景。为了弥补这个缺陷,Java 提供了 SecureRandom,SecureRandom.getInstanceStrong() 会读取 Linux 中的 /dev/random 生成种子。 /dev/random 是一个阻塞数字生成器,通过监听键盘和鼠标输入以及磁盘活动等活动来生成随机数,当没有足够的随机事件时,就会阻塞 JVM,表现就是程序卡在 SecureRandom.getInstanceStrong() 这个方法,进一步会造成接口相应慢,超时重试、数据库连接池里没有可用连接等等一堆问题。这个问题在 k8s 集群中尤其明显,因为很多服务可能都都在消耗随机数。

进一步看,生成随机数最终调用的是 sun.security.provider.NativePRNG.generateSeed() 方法,这个方法读取的是上文提到的 /dev/random。 与此相对的,是sun.security.provider.NativePRNG.generateSeed() 方法,它读取的是 /dev/urandom ,这个方法不会导致阻塞。虽然 NativePRNG 是阻塞的,但是一些资料上提到它的性能会好一些, web 应用还是要使用这个方法。
SecureRandom.getInstanceStrong() 方法会根据 JVM 运行的系统,去调用它所认为最优的方法,比如在 Linux 系统中,调用的就是 sun.security.provider.NativePRNG.generateSeed()。

关于熵值的常见命令:
cat /proc/sys/kernel/random/poolsize //查看熵池容量

cat /proc/sys/kernel/random/entropy_avail && date //打印可用的熵及日期

//每个2s打印一下可用的熵,方便看熵的使用情况
while(true) do cat /proc/sys/kernel/random/entropy_avail && date sleep 2 done
正常情况下的熵:熵值一直是等于熵池容量的,这里的熵池容量是3754
img
异常情况下的熵。可以看到熵值有明显抖动,根据经验,熵池下降到 2000 左右,阻塞就会非常严重了
img

参考资料:

使用 SecureRandom 产生随机数采坑记录
https://cloud.tencent.com/developer/article/1558293
随机数SecureRandom在Linux下阻塞
https://segmentfault.com/a/1190000039268233
Proper use of Java SecureRandom
https://www.synopsys.com/blogs/software-security/proper-use-of-javas-securerandom.html
关于熵的一些命令
https://cloud.tencent.com/developer/article/1634848