FunTester UUID 意想不到的 block

FunTester · 2023年11月06日 · 2466 次阅读

UUID(Universally Unique Identifier,通用唯一标识符)是一种用于标识信息的 128 位标识符。Java 开发人员倾向于使用 java.util.UUID#randomUUID API 来生成 UUID 编号(类似 4c88314f-14ca-4652-8567-4471a0ef917c)。

UUID 通常用于标识数据记录、会话、文件、对象等,以确保它们在不同上下文中的唯一性。注意,UUID 是一种全局唯一性标识符,不保证在不同时间生成的 UUID 之间是有序的或可比较的,因此不应该依赖于 UUID 的大小或顺序。

在某些情况下,使用这个 API 可能对应用程序的可用性产生负面影响。下面,我们将通过一个实际案例来深入讨论这一问题。

randomUUID 如何工作

java.util.UUID#randomUUID API 在内部使用操作系统中的entropy来生成一个唯一的数字。entropy 是什么意思 Linux 内核使用某些技术,如用户的鼠标移动,硬件风扇噪音的变化,设备驱动程序噪音的变化,来生成随机数。当操作系统中缺乏时,随机数生成将减慢。当出现减速时,调用此 java.util.UUID#randomUUID 的应用程序线程将被置于 BLOCKED 状态,严重时会让程序处于暂停状态。

真实的世界应用程序-java.util.UUID#randomUUID()API 中阻塞的 50 个线程

下面是一个应用程序的实际线程转储报告,该应用程序正遭受此问题的困扰。在线程转储报告中,我们可以注意到总共有 102 个线程。在这 102 个线程中,有 50 个线程由于java.util.UUID#randomUUID API 而处于 BLOCKED 状态。下面是这 50 个线程之一的堆栈跟踪:

"[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'" waiting for lock java.security.SecureRandom@20a56b2b BLOCKED

java.security.SecureRandom.nextBytes(SecureRandom.java:433)
java.util.UUID.randomUUID(UUID.java:159)
com.buggycompany.jtm.bp.<init>(bp.java:185)
com.buggycompany.jtm.a4.f(a4.java:94)
com.buggycompany.agent.trace.RootTracer.topComponentMethodBbuggycompanyin(RootTracer.java:439)
weblogicx.servlet.gzip.filter.GZIPFilter.doFilter(GZIPFilter.java)
weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3730)
weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3696)
weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2273)
weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2179)
weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1490)
weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
weblogic.work.ExecuteThread.run(ExecuteThread.java:221)

由于缺少 entropy,线程在调用 java.util.UUID#randomUUID 时进入了 BLOCKED 状态,无法继续执行代码。这 50 个线程被卡住了。

解决方案

JDK 升级

这个问题是由 Java 中的一个已知 bug 引起的。但是,自 JDK 8 u112 或 JDK 9 b105 以来,它已被修复。所以最优先的解决方案就是升级你的 JDK 版本。

Linux 安装 Haveged

如果你的 Java 程序运行在 Linux 中,那么可以考虑安装haveged库。haveged 项目旨在提供一个易于使用的,不可预测的随机数生成器,基于HAVEGE算法的适应。这里是Haveged项目 GIT 仓库页面。以下是如何安装它:

在基于 Debian 的平台(Debian,Ubuntu)上:

sudo apt-get install rng-tools
sudo update-rc.d haveged defaults

在 Redhat 平台(RHEL、Fedora、CentOS)上:

sudo yum install rng-tools
sudo chkconfig haveged on

用/dev/urandom 代替/dev/random

类 Unix 操作系统提供了特殊的文件/dev/random,用作伪随机数生成器。Java 使用这个文件来生成随机数。可以将其配置为使用/dev/urandom而不是/dev/random

/dev/urandom是另一个能够生成随机数的特殊文件。然而,由于随机性较小,它具有降低安全性的缺点。如果需要的话,可以通过在启动过程中将下面的 JVM 参数传递给你的 Java 程序来实现它:

-Djava.security.egd=file:/dev/./urandom
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册