java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java SecureRandom

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

作者:firechili

本文主要介绍了Docker容器中因熵池不足导致SecureRandom.getInstanceStrong()严重阻塞,引发接口超时、线程池耗尽的问题,感兴趣的可以了解一下

背景

一个看似简单的小说推荐功能,在本地开发和云服务器直接部署时都运行良好,但一旦迁移到 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)

浏览器端表现

原因分析

1. SecureRandom.getInstanceStrong() 的工作原理

SecureRandom.getInstanceStrong() 是 Java 提供的密码学安全随机数生成器,它会:

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

2. 熵池(Entropy Pool)机制

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

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

查看当前熵池大小:

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

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

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

容器的熵池问题:

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

解决方案

方案一:改用 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()可能严重阻塞,导致应用超时或线程池耗尽

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

这是一个历史遗留问题:

其他语言的类似陷阱

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

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

您可能感兴趣的文章:
阅读全文