使用 SecureRandom 产生随机数采坑记录
公众号「码海」欢迎关注
背景
我们的项目工程里经常在每个函数需要用到 Random 的地方定义一下 Random 变量(如下)
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 效率太低了,而且由于 JDK 版本的不同,可能 Random 产生的随机数不够随机。为了提升性能和随机性,建议定义一个 Random 单例来统一产生随机数, Sonar 建议使用 SecureRandom.getInstanceStrong() 来初始化,如下
private Random rand = SecureRandom.getInstanceStrong();
于是我们就将其改成 sonar 建议的形式来生成随机数
问题初现
结果问题来了,下午在我们业务的接口上,第三方反馈接口调用超时,从我们的监控来看,接口执行阻塞,看起来像陷入了某种死循环。

定位问题
复现问题:首先使用了相同的请求参数在预发进行了测试,但令人不解的是,问题无法复现。随后又测试了线上机器,可以稳定的复现问题。这时一脸黑人问号。
打日志:为了定位问题,在代码的关键位置插入日志,经过多次的发布定位到问题,执行到 SecureRandom.getInstanceStrong() 方法后就阻塞了。
原因剖析
SecureRandom.getInstanceStrong() 方法在 linux 环境下使用 /dev/random 生成种子。但是 /dev/random 是一个阻塞数字生成器,如果它没有足够的随机数据提供,它就一直等,这迫使 JVM 等待。键盘和鼠标输入以及磁盘活动可以产生所需的随机性或熵。但在一个缺乏这样的活动服务器,可能会出现问题,当系统的熵池中数量不足时,就会阻塞当前线程。
那么 Linux 中随机数是如何产生的呢 PRNG(Pseudo-Random Number Generator),以下解释摘自 IBM 的文章
Linux 内核采用熵来描述数据的随机性,熵(entropy)是描述系统混乱无序程度的物理量,一个系统的熵越大则说明该系统的有序性越差,即不确定性越大。内核维护了一个熵池用来收集来自设备驱动程序和其它来源的环境噪音。理论上,熵池中的数据是完全随机的,可以实现产生真随机数序列。为跟踪熵池中数据的随机性,内核在将数据加入池的时候将估算数据的随机性,这个过程称作熵估算。熵估算值描述池中包含的随机数位数,其值越大表示池中数据的随机性越好。 内核中随机数发生器 PRNG 为一个字符设备 random,代码实现在 drivers/char/random.c,该设备实现了一系列接口函数用于获取系统环境的噪声数据,并加入熵池。系统环境的噪声数据包括设备两次中断间的间隔,输入设备的操作时间间隔,连续磁盘操作的时间间隔等。 对应的接口包括
void add_device_randomness(const void *buf, unsigned int size);
void add_input_randomness(unsigned int type, unsigned int code,
unsigned int value);
void add_interrupt_randomness(int irq, int irq_flags);
void add_disk_randomness(struct gendisk *disk);
内核提供了 1 个的接口来供其他内核模块使用。
该接口会返回指定字节数的随机数。random 设备了提供了 2 个字符设备供用户态进程使用——/dev/random 和/dev/urandom:
- /dev/random 适用于对随机数质量要求比较高的请求,在熵池中数据不足时, 读取 dev/random 设备时会返回小于熵池噪声总数的随机字节。/dev/random 可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random 的读操作将会被阻塞,直到收集到了足够的环境噪声为止。这样的设计使得/dev/random 是真正的随机数发生器,提供了最大可能的随机数据熵。
- /dev/urandom,非阻塞的随机数发生器,它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom 的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random 的。它可以作为生成较低强度密码的伪随机数生成器,对大多数应用来说,随机性是可以接受的。
解决方案
有了以上的的解释,我们就知道解决方案了,使用 /dev/urandom 这种非阻塞的方式来产生随机数即可,在 Java 中我们这样写即可
SecureRandom random = new SecureRandom();
new SecureRandom() 使用 /dev/urandom 生成种子,不会产生阻塞。
参考文章
使用 SecureRandom 产生随机数采坑记录的更多相关文章
- Charles 抓包工具安装和采坑记录
Charles 抓包工具安装和采坑记录 网络抓包是解决网络问题的第一步,也是网络分析的基础.网络出现问题,第一步肯定是通过抓包工具进行路径分析,看哪一步出现异常.做网络爬虫,第一步就是通过抓包工具对目 ...
- Antd前端开发采坑记录
背景 基于页面友好,界面整洁美观:基于Antd框架开发虾能平台 选型 基于Antd-admin工程架构,进行开发:基于Antd+React+Umj 采坑记录 按照Html方式天机onClick方法,每 ...
- HUE Oozie : error=2, No such file or directory采坑记录
HUE Oozie : error=2, No such file or directory采坑记录 1.错误详情 一直都是同一种方式在hue上定义workflow,不知为啥 今天定义的就是不行... ...
- uni-app采坑记录
1. uni-app采坑记录 1.1. 前言 这里记录下uni-app实践中踩的坑 1.2. 坑点 1.2.1. 触发事件@longTap和@longpress 这两个都表示长按触发事件,那么这两个有 ...
- angular采坑记录
在angular中会遇到一些莫名的问题,导致不能完成想要的功能,可能是某项用法使用错误,或许是angular相对应不支持,或者是我们功力根本就没有达到.为了在每次采坑之后能有所收获,再遇到时能理解其根 ...
- v8环境搭建采坑记录
项目组有把js接入C++服务求的需求,故开始了v8接入的工作,用了一天多时间,v8才在centos环境上成功安装,过程中踩了很多坑,下面将采坑过程记录如下: centos下编译安装v8: 查看ce ...
- Win7 node多版本管理gnvm采坑记录
采坑描述:下载新node版本及切换node失败 解决:1.要用管理员权限启动cmd:2.确保node是空闲的 Gnvm下载地址: 32-bit | 64-bit Github 1.下载之后为 得到一个 ...
- Android Studio采坑记录
折腾了几个月的Android Studio,终于在今天被我搞定了 ( ̄▽ ̄)~* 开贴记录下,免得下次再次采坑 先说下我之前电脑的环境配置吧,sdk是几年前在网上下载别人整理出来的包,一直没有更新过 ...
- golang采坑记录
安装golang,引入第三方库,采坑 1.获取安装包 go语言中文网:https://studygolang.com/dl 官网地址:https://studygolang.com/dl 2.下载 选 ...
随机推荐
- Redis开发与运维:SDS
STRING 我们会经常打交道的string类型,在redis中拥有广泛的使用.也是开启redis数据类型的基础. 在我最最开始接触的redis的时候,总是以为字符串类型就是值的类型是字符串. 比如: ...
- mine:dp
一个小的线性dp.方法很多,八仙过海各显神通. 我想讲一下我的: #include<cstdio> #define mod 1000000007 ];][][],n;//是不是雷,右边有没 ...
- %%%GXZ大佬回关
- js+jquery 笔记
本人也不知道该怎么写javascript笔记,本人就拿一个例子来写,这个例子就从算法所说起吧! 例如: 一个整数的数组 [122,393,664,935,1386,1675,1928,2199,247 ...
- php修改网站默认编码
php修改网站默认编码网站如果header 不指定utf8默认 不是utf8 所以输入中文显示会乱码 一般都是apache不是不是utf8 打开 apache 配置文件 httpd.conf 加个 A ...
- PHP判断数组下标有没有存在的方法
PHP判断数组下标有没有存在的方法<pre>if(!empty($token['errcode'])){ print_r($token['errmsg']); exit();}</p ...
- php imagick svg转成jpg
php imagick svg转成jpg <pre> public function svgtojpg() { $image = '<?xml version="1.0&q ...
- go中的数据结构通道-channel
1. channel的使用 很多文章介绍channel的时候都和并发揉在一起,这里我想把它当做一种数据结构来单独介绍它的实现原理. channel,通道.golang中用于数据传递的一种数据结构.是g ...
- SpringBoot系列教程JPA之指定id保存
原文链接: 191119-SpringBoot系列教程JPA之指定id保存 前几天有位小伙伴问了一个很有意思的问题,使用 JPA 保存数据时,即便我指定了主键 id,但是新插入的数据主键却是 mysq ...
- lqb 入门训练 A+B问题
入门训练 A+B问题 时间限制:1.0s 内存限制:256.0MB 问题描述 输入A.B,输出A+B. 说明:在“问题描述”这部分,会给出试题的意思,以及所要求的目标. 输入格式 输入的第 ...