UUID意想不到的block

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章