https://juejin.cn/post/6877132677854527495
問題場景
在某一時刻,某個微服務的某個例項 CPU 負載突然飈高:
同時建立了很多資料庫連結:
其他例項沒有這個現象。
問題定位
由於建立了很多資料庫連結,猜想可能是資料庫比較慢,檢視資料庫這段時間的 SQL 統計,發現資料庫並不慢:
其中這個微服務這段時間的熱點 SQL,執行並不慢。那麼問題出在了哪裡呢?可能是由於 GC,可能是由於 safepoint,還有可能是獲取鎖時間過長(參考:Java 監控 JFR全解),我們 dump 一下 JFR 並檢視其中的 safepoint,GC 以及 Monitor Blocked 相關事件。
首先檢視GC,發現都是 Young GC, GC 暫停時間也可以接受。
然後是 safepoint,雖然有采集到 safepoint,但是暫停時間也沒有很長。
最後檢視 Java Monitor Block,發現有很多很長時間的鎖等待:
堆疊顯示,阻塞在:void sun.security.provider.SecureRandom.engineNextBytes(byte[])
上面,這就是一個經典的問題,Java Random,參考程式碼:NativePRNG
// name of the *System* property, takes precedence over PROP_RNDSOURCE
private static final String PROP_EGD = "java.security.egd";
// name of the *Security* property
private static final String PROP_RNDSOURCE = "securerandom.source";
private static final boolean useLegacyDSA =
Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty
("jdk.security.legacyDSAKeyPairGenerator"));
static final String URL_DEV_RANDOM = "file:/dev/random";
static final String URL_DEV_URANDOM = "file:/dev/urandom";
涉及到兩種隨機數 seed 生成方式,一種是"file:/dev/random",另一種是"file:/dev/urandom",透過設定系統屬性java.security.egd
指定,預設是"file:/dev/random"
兩種 Random 原理與解決
在 Linux 4.8 之前:
在 Linux 4.8 之後:
在熵池不夠用的時候,預設的"file:/dev/random"會阻塞,"file:/dev/urandom"不會,繼續用。對於我們來說,"file:/dev/urandom"夠用,所以透過-Djava.security.egd=file:/dev/./urandom
設定系統屬性,使用 urandom 來減少阻塞。