当前位置: 代码网 > it编程>编程语言>Java > Java中随机数陷阱SecureRandom的问题解决

Java中随机数陷阱SecureRandom的问题解决

2026年07月28日 Java 我要评论
背景一个看似简单的小说推荐功能,在本地开发和云服务器直接部署时都运行良好,但一旦迁移到 docker 容器环境,接口响应时间从毫秒级飙升到几十秒甚至超时,最终导致后端服务假死。只有重启容器才能恢复。经

背景

一个看似简单的小说推荐功能,在本地开发和云服务器直接部署时都运行良好,但一旦迁移到 docker 容器环境,接口响应时间从毫秒级飙升到几十秒甚至超时,最终导致后端服务假死。只有重启容器才能恢复。

经过系列排查,找到罪魁祸首如下:

random rand = securerandom.getinstancestrong();

问题现象

本地 windows/mac 开发环境:推荐接口响应 < 10ms 
云服务器直接部署(centos 8):推荐接口响应 < 20ms 
docker 容器部署(同一台 centos 8):推荐接口响应 5s~60s+,高并发时线程池耗尽 

错误日志

2026-06-30 18:35:18.123 error [http-nio-9091-exec-42] 
org.apache.catalina.core.containerbase.[tomcat].[localhost].[/api] 
servlet.service() for servlet [dispatcherservlet] threw exception

java.lang.illegalstateexception: thread blocked waiting for entropy
    at java.base/java.security.securerandom.nextbytes(securerandom.java)
    at org.example.chyznovel.service.impl.bookserviceimpl.listrecbooks(bookserviceimpl.java:202)

浏览器端表现

  • options 预检请求返回 504 gateway time-out
  • 实际 post 请求偶尔成功,但大部分超时
  • nginx 日志显示大量 upstream timeout

原因分析

1. securerandom.getinstancestrong() 的工作原理

securerandom.getinstancestrong() 是 java 提供的密码学安全随机数生成器,它会:

  • linux 系统:读取 /dev/random 设备文件
  • windows 系统:调用 cryptoapi
  • macos 系统:读取 /dev/urandom

关键问题在于:/dev/random 是阻塞式的真随机数源。

2. 熵池(entropy pool)机制

linux 内核维护一个"熵池",收集系统中的各种"噪音"作为随机数种子:

  • 键盘敲击时间间隔
  • 鼠标移动轨迹
  • 硬盘读写延迟
  • 网络包到达时间
  • 中断事件

当应用程序从 /dev/random 读取数据时:

  • 熵池充足:立即返回随机数
  • 熵池不足:阻塞等待,直到收集到足够的熵

查看当前熵池大小:

cat /proc/sys/kernel/random/entropy_avail
# 正常值:> 1000
# 危险值:< 100(此时读取 /dev/random 会阻塞)

3. 为什么容器环境特别容易触发?

环境熵源丰富度是否容易阻塞
本地开发机 丰富(有鼠标、键盘、gui)
物理服务器 较丰富(有硬盘、网络、中断)偶尔
docker 容器贫乏(隔离环境,无外设)极易

容器的熵池问题:

  • 隔离性导致熵源减少:容器内没有鼠标、键盘等交互设备
  • 共享宿主机熵池:多个容器竞争同一个宿主机的熵资源
  • 启动初期熵池为空:容器刚启动时熵池几乎为零,需要时间积累
  • 高并发放大问题:每个线程调用 getinstancestrong() 都会消耗熵,快速耗尽池子

4. 为什么本地和直接部署没问题?

  • 本地开发:你的电脑有鼠标、键盘、浏览器等大量源,熵池始终充足
  • 云服务器直接部署:虽然没有 gui,但有网络流量、磁盘 i/o、定时中断等熵源,基本够用
  • 容器部署:熵源被大幅削减,加上多容器竞争,极易触发阻塞

解决方案

方案一:改用 threadlocalrandom(推荐)

适用场景:推荐算法、游戏逻辑、a/b 测试等不需要密码学安全的场景

方案二:使用非阻塞的 securerandom

适用场景:必须用强随机数,但不能接受阻塞

方案三:增加容器熵源(治标不治本)

如果确实需要用 securerandom.getinstancestrong(),可以优化容器熵池:
1. 安装 haveged(熵守护进程)

# dockerfile
from openjdk:21-slim
run apt-get update && \
    apt-get install -y haveged && \
    apt-get clean
cmd ["haveged", "-w", "1024", "-v", "1", "--foreground"]
# docker-compose.yml
services:
  app:
    image: myapp
    cap_add:
      - sys_admin  # haveged 需要特权

2. 挂载宿主机的 /dev/random

# docker-compose.yml
services:
  app:
    volumes:
      - /dev/random:/dev/random
      - /dev/urandom:/dev/urandom

什么时候必须用 securerandom?

必须用的场景(密码学安全要求):

  1. 生成加密密钥(aes、rsa)
  2. 生成 jwt token 签名密钥
  3. 生成密码盐值(salt)
  4. 生成 csrf token
  5. 生成一次性验证码(防止暴力破解)
  6. 彩票/赌 博系统开奖(法律要求不可预测)

 不需要用的场景(伪随机即可):

  1. 推荐算法随机排序
  2. 游戏中的随机掉落
  3. a/b 测试分组
  4. 负载均衡随机选择
  5. 模拟数据生成
  6. 任何"即使被猜到也没关系"的场景

 随机数生成器选型指南

场景推荐方案理由
密钥 / token 生成new securerandom()需要强随机性,但频率低,阻塞可接受
验证码生成new securerandom()防暴 力 破 解,需要不可预测性
推荐 / 游戏 / a-b 测试threadlocalrandom.current()高频调用,性能优先,伪随机够用
ssl / tls 握手new securerandom()jdk 内部已优化,无需手动干预
⚠️ 绝对不能用(除非有特殊强随机需求且能接受阻塞)securerandom.getinstancestrong()可能严重阻塞,导致应用超时或线程池耗尽
  • 在 docker 容器等低熵环境中,即使你用了 new securerandom(),也建议 配置                     -djava.security.egd=file:/dev/urandom,避免因熵池不足导致阻塞。
  • 如果你用的是 物理服务器 且业务对性能极度敏感(如高并发网关),对于非安全类随机(如路由分发、采样),优先用 threadlocalrandom。
  • securerandom.getinstancestrong() 在容器环境几乎等于 自 杀式阻塞,除非你明确知道自己在做什么,否则坚决避开。(以后再也不乱抄网上的了嘤嘤嘤!)

为什么 java 不默认用非阻塞的?

这是一个历史遗留问题:

  • 安全性优先:java 设计者认为"宁可慢,不能不安全"
  • 向后兼容:改变默认行为可能影响现有应用
  • 开发者责任:框架提供工具,具体选型由开发者决定

其他语言的类似陷阱

  • python:os.urandom() vs random.systemrandom()
  • node.js:crypto.randombytes()(异步非阻塞)vs crypto.pseudorandombytes()
  • go:crypto/rand(阻塞)vs math/rand(非阻塞)

通用原则:区分"密码学安全随机数"和"普通伪随机数"的使用场景。

到此这篇关于java中随机数陷阱securerandom的问题解决的文章就介绍到这了,更多相关java securerandom内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com