問題起因
一個對外提供的接口,中間需要調用第三方接口,涉及到三方機密問題,其中使用到了安全隨機數
之前的寫法如下
public static String randomCode() { Random r = new Random(); StringBuilder str = new StringBuilder(); for (int i=0;i<6;i++){ str.append((r.nextInt(10))); } return str.toString(); }
被solar掃面到不符合規范於是就改成下面的:
public class CodeUtil { private CodeUtil() {} // SecureRandom is preferred to Random private static Random rand; static { try { rand = SecureRandom.getInstanceStrong(); } catch (NoSuchAlgorithmException e) { log.error("NoSuchAlgorithmException: {}", e); } } public static String randomCode() { StringBuilder str = new StringBuilder(); for (int i=0;i<6;i++){ str.append((rand.nextInt(10))); } return str.toString(); } }
bug現象
之前所有調用對外暴漏的服務的時候都是正常的,第二天莫名其妙的報錯
根據這個報錯很自然就是想到服務調用超時,於是各種設置feign調用超時時間,但是都沒有起到效果,本地測試服務調用時間,發現時間開銷也並不是很高,總是感覺客戶端鏈接一到服務端就斷開了,於是估計因該是服務端的問題,但是本地調用也沒有問題,甚至在服務中sleep了30s,依然沒有顯示超時,排查了一個下午,也沒有搞明白到底是什么錯誤
曙光
當時也不知道是什么力量讓我在服務器上打出了一個top命令,發現了一個叫java的進程,pid為1,於是就jstack了一下,驚喜的返現下面這些懂
仔細觀察了一下發現所有的http線程都阻塞在了同一把鎖上面,這個時候問題基本已經定位到了
於是還原了一下源代碼,問題成功解決。
SecureRandom.getInstanceStrong(); 是jdk1.8里新增的加強版隨機數實現
如果你的服務器在Linux操作系統上,這里的罪魁禍首是SecureRandom generateSeed()。它使用/dev/random生成種子。但是/dev/random是一個阻塞數字生成器,如果它沒有足夠的隨機數據提供,它就一直等,這迫使JVM等待。鍵盤和鼠標輸入以及磁盤活動可以產生所需的隨機性或熵。但在一個服務器缺乏這樣的活動,可能會出現問題
有2種解決方案:
1. 在Tomcat環境中解決:
可以通過配置 JRE 使用非阻塞的 Entropy Source:
在 catalina.sh 中加入這么一行:-Djava.security.egd=file:/dev/./urandom 即可。
2. 在 JVM 環境中解決(本人使用此方法):
打開jdk安裝路徑 $JAVA_PATH/jre/lib/security/java.security 這個文件,找到下面的內容:
securerandom.source=file:/dev/random
替換成:
securerandom.source=file:/dev/./urandom
問題完美解決
這里值為何要在 dev 和 random 之間加一個點呢?是因為一個 JDK 的 bug,有人反饋即使對 securerandom.source 設置為
/dev/urandom 它也仍然使用的 /dev/random,有人提供了變通的解決方法,其中一個變通的做法是對 securerandom.source
設置為 /dev/./urandom 才行。也有人評論說這個不是 bug,是有意為之。
————————————————
版權聲明:本文為CSDN博主「你們都是壞孩子00」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wmq930626/java/article/details/105951216