首頁>技術>

每個人都是在不斷碰壁中獲得成長,bug的逼Grand SantaFe高, 成長速度越快。

本人上週親手寫下了一個牛逼的bug,直接導致的結果是,晚上12點升級後臺介面以後,第二天早上7點多開始,所有的app頁面出現卡頓,白屏。

公司研發老總,迅速召集公司運維大佬,產品大佬,研發大佬奔赴公司解決bug。

所有人,開始手忙腳亂,檢視線上日誌,抓包,阿爾薩斯監聽 介面耗時。各個大神,各種手段,各顯才能……

經過三個小時的排查,最終用jstack 命令檢視執行緒數,發現整個服務,執行緒不斷攀升至400多,且絕大多數空閒執行緒一直處於等待狀態,沒有執行任何任務。

弔詭的地方就在這裡,緊急著,進入執行緒號裡面,發現列印了如下堆疊資訊:

當我看到SecureRandom這個關鍵字時,恍然大悟,這不是前幾天通過solar進行程式碼檢測時,剛剛修改的類嗎?發生了什麼?

執行緒安全?執行緒阻塞?有關執行緒的各個關鍵詞在大腦裡一閃而過……

於是迅速恢復程式碼成原先的Random r = new Random()重新構建服務,app恢復正常,臥槽……什麼鬼

首先去看soloar程式碼檢測給出的建議是這樣的:

Noncompliant Code Example

public void doSomethingCommon() { Random rand = new Random(); // Noncompliant; new instance created with each invocation int rValue = rand.nextInt(); //...

Compliant Solution

private Random rand = SecureRandom.getInstanceStrong(); // SecureRandom is preferred to Randompublic void doSomethingCommon() { int rValue = this.rand.nextInt(); //...

當時未進行更多的思考,直接把SecureRandom.getInstanceStrong();寫在了方法內,引發了執行緒阻塞。

今天再一次查SecureRandom.getInstanceStrong();這個方法時,才發現如下問題:

SecureRandom.getInstanceStrong(); 是jdk1.8裡新增的加強版隨機數實現。

如果你的伺服器在Linux作業系統上,這裡的罪魁禍首是SecureRandom generateSeed()。

它使用/dev/random生成種子。

但是/dev/random是一個阻塞數字生成器,如果它沒有足夠的隨機資料提供,它就一直等,這迫使JVM等待。

鍵盤和滑鼠輸入以及磁碟活動可以產生所需的隨機性或熵。

但在一個伺服器缺乏這樣的活動,可能會出現問題。

//////////

1,如果是tomcat 環境,有如下解決方式

可以通過配置JRE使用非阻塞的Entropy Source:

在catalina.sh中加入這麼一行:-Djava.security.egd=file:/dev/./urandom 即可。

加入後再啟動Tomcat,整個啟動耗時下降到Server startup in 20130 ms。

這種方案是在修改隨機數獲取方式,那這裡urandom是啥呢?

/dev/random的一個副本是/dev/urandom(“unblocked”,非阻塞的隨機數發生器[4]),它會重複使用熵池中的資料以產生偽隨機資料。

這表示對/dev/urandom的讀取操作不會產生阻塞,但其輸出的熵可能小於/dev/random的。

它可以作為生成較低強度密碼的偽隨機數生成器,不建議用於生成高強度長期密碼。- - - wikipedia

在JVM環境中解決

開啟$JAVA_PATH/jre/lib/security/java.security這個檔案,找到下面的內容:

securerandom.source=file:/dev/random

替換成

securerandom.source=file:/dev/./urandom

2 springboot專案,網上發現很多朋友啟動時加了啟動引數:

-Djava.security.egd=file:/dev/./urandom

但是沒有起作用

去查 NativePRNG$Blocking的程式碼,看到它的文件描述:

A NativePRNG-like class that uses /dev/random for both seed and random material. Note that it does not respect the egd properties, since we have no way of knowing what those qualities are.

奇怪怎麼-Djava.security.egd=file:/dev/./urandom引數沒起作用,仍使用/dev/random作為隨機數的熵池,時間久或呼叫頻繁的話熵池很容易不夠用而導致阻塞;於是看了一下 SecureRandom.getInstanceStrong()的文件:

Returns a SecureRandom object that was selected by using the algorithms/providers specified in the securerandom.strongAlgorithms Security property.

原來有自己的演算法,在 jre/lib/security/java.security 檔案裡,預設定義為:

securerandom.strongAlgorithms=NativePRNGBlocking:SUN

如果修改演算法值為NativePRNGNonBlocking:SUN的話,會採用NativePRNG$NonBlocking裡的邏輯,用/dev/urandom作為熵池,不會遇到阻塞問題。

但這個檔案是jdk系統檔案,修改它或重新指定一個路徑都有些麻煩,最好能通過系統環境變數來設定,可這個變數不像securerandom.source屬性可以通過系統環境變數-Djava.security.egd=xxx來配置,找半天就是沒有對應的系統環境變數。

只好修改程式碼,不採用SecureRandom.getInstanceStrong這個新方法,改成了SecureRandom.getInstance("NativePRNGNonBlocking")。

整理一下知識點

1.SecureRandom本身並不是偽隨機演算法的實現,而是使用了其他類提供的演算法來獲取偽隨機數。

2.如果簡單的new一個SecureRandom物件的話,在不同的操作平臺會獲取到不同的演算法,windows預設是SHA1PRNG,Linux的話是NativePRNG。

3. Linux下的NativePRNG,如果呼叫generateSeed()方法,這個方法會讀取Linux系統的/dev/random檔案,這個檔案在JAVA_HOME/jre/lib/securiy/java.security裡面有預設定義。而/dev/random檔案是動態生成的,如果沒有資料,就會阻塞。也就造成了第一個現象。

4.可以使用-Djava.security.egd=file:/dev/./urandom (這個檔名多個u)強制使用/dev/urandom這個檔案,避免阻塞現象。中間加個點的解釋是因為某個JDK BUG,SO那個帖子有連結。

5.如果使用SecureRandom.getInstanceStrong()這種方法初始化SecureRandom物件的話,會使用NativePRNGBlocking這個演算法,而NativePRNGBlocking演算法的特性如下:

NativePRNGBlocking uses /dev/random for all of the following operations: Initial seeding: This initializes an internal SHA1PRNG instance using 20 bytes from /dev/random Calls to nextBytes(), nextInt(), etc.: This provides the XOR of the output from the internal SHA1PRNG instance (see above) and data read from /dev/random Calls to getSeed(): This provides data read from /dev/random 

可見這個演算法完全依賴/dev/random,所以當這個檔案隨機數不夠的時候,自然會導致卡頓了。

6.如果使用NativePRNGBlocking演算法的話,4中的系統引數失效!!!(這個是從http://hongjiang.info/java8-nativeprng-blocking/看到的)

7.一般使用SecureRandom不需要設定Seed,不需要設定演算法,使用預設的,甚至一個靜態的即可,如果有需求的話可以在執行一段時間後setSeed一下

文章參考

1:http://hongjiang.info/java8-nativeprng-blocking/

2:https://blog.csdn.net/bigtree_3721/article/details/85085413

3:https://blog.csdn.net/upshi/article/details/54907464

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 使用 Python Flask 快速搭建前端除錯後臺