记录SecureRandom踩坑经历
背景
上周工作中有个问题耽搁了很长时间,打算写个博客记录一下,算是一个小复盘。
问题
我们公司共有三个测试环境,暂且称为test1、test2、test3环境。有一个服务发布在test3环境可以正常使用,没有任何报错,但是发布在test2环境就会报错。代码都是一样的,刚开始是怀疑配置有问题,仔细检查之后没有任何问题。后来借助arthas定位到了问题在于SecureRandom。也是因为这个事情,也专门写了一篇博客记录下。
SecureRandom
使用随机数的话,一般会像这样:
public void doSomethingCommon() {
Random rand = new Random();
...
}
但是呢,这样使用的话,sonar会给你一个告警:
Creating a new Random object each time a random value is needed is inefficient and may produce numbers which are not random depending on the JDK. For better efficiency and randomness, create a single Random, then store, and reuse it.
大意就是说不能每个方法都创建 Random生成随机数,因为效率太低,应该定义Random单例统一生成。并且明确建议使用SecureRandom.getInstanceStrong() 来初始化,就像这样:
private Random rand = SecureRandom.getInstanceStrong();
ok,照你说的改,改完之后就出现了上面的问题,test3环境是好的,test2就是不行,只有上了arthas才定位到问题所在,执行到 SecureRandom.getInstanceStrong() 方法后就线程就阻塞掉了,这就很拉胯了。
关于SecureRandom,讲的很清楚了,大概是因为SecureRandom在生成随机数的时候,会判断当前服务器环境的一些属性值,鼠标、键盘输入这些都算,所以就和环境有很强的相关性了,如果没有足够的环境属性用于生成随机数,就会一直等待,也就迫使JVM等待了。
源码中显示:
SecureRandom secureRandom = new SecureRandom();
会指定seed = 0,而sonar推荐的方式没有指定这个值,我猜差别应该是在这地方,总之new的方式不会导致阻塞,当然了,这是我猜的。
总之这种因为环境不同导致的问题,排查起来还是很糟心的。首先想吐槽下sonar,也是因为它的扫描和推荐才导致我排查这个了好久,其次呢也算吃一堑长一智,以后遇到这种问题也大概有个思路了,还有就是arthas又熟了一些,活到老学到老哈哈~~
参考资料:
