完全解決Tomcat啓動速度慢的問題

問題現象

美女同事找我解決一個問題,說Tomcat啓動很慢。開始我覺得是程序寫的問題,因此把webapps下全部程序都刪除掉。(只保留Tomcat自帶)靈異的事情發生了,Tomcat中止在——java

完全解決Tomcat啓動速度慢的問題



我查看了一下進程,Tomcat所在的JVM進程已經被啓動了因此能夠排除是JVM退出引發的問題。那麼問題真的就是JVM由於某種緣由被阻塞了。web

分析

問題比較棘手,我排除了CPU、內存不足引發的問題;排除了硬盤空間不足引發的問題;我甚至去觀察了網絡I/O、硬盤I/O狀況,都很是正常。程序被阻塞通常來講必定是要等待某個資源,而如今的狀況是全部資源都充足,因此我幾乎想不到是什麼問題引發的。算法

我開始懷疑是KVM Hypervisor虛擬化的問題(用的是虛擬機)我改變了策略在VMWare開了兩臺虛擬機上直接下載Tomcat啓動。其中一臺很快啓動,另外一臺竟然也被阻塞,問題被重現了。shell

眼看要在美女面前丟臉,我光輝偉岸的形象要蕩然無存。這種狀況下我不能去「擼」代碼吧?何況Tomcat那麼多人用,真有這麼明顯的Bug早就炸開鍋了。(Tomcat仍是很靠譜的不像xxxxStack那麼狗屎)仔細想一想我須要找到Tomcat中止在了哪裏?代碼裏發生了什麼事情,可是我又不可能去擼代碼。迫不得已的狀況下我決定試一下 strace,這是一個跟蹤系統調用(System Call)的工具,不管是Java仍是Pyhton不少資源申請都會變成都會變成System Call。(好比打開文件、新建線程、讀寫數據、等待I/O)經過這個工具我至少能夠知道Tomcat是中止在哪一個System Call上的,這樣能夠方便我推斷出問題的緣由。apache

strace -f -o strace.out ./catalina.sh run

strace有不少參數,我用了二個參數tomcat

  • -f 跟蹤fork的子進程,通俗的說會跟蹤全部線程的系統調用安全

  • -o把內容輸出到文件網絡

其餘參數請自行搜索架構

下面分析strace.out文件,分析的方法是從下往上(被阻塞的地方確定是在最後咯)。首先咱們須要去掉Tomcat中止引發的System Call,它們不是咱們須要的。從後往前搜索找到SIGINTapp

完全解決Tomcat啓動速度慢的問題


紅色部分以上就是引發阻塞的系統調用了,上面有一大堆一大堆的futex的調用,它是Linux中的一種輕量級的同步方法,因此咱們能夠判斷出最上面確定是有某個System Call就是阻塞的真正元兇。跳過全部的futex:

完全解決Tomcat啓動速度慢的問題


這個read就是引發後面一串futex的真正緣由,strace很是聰明它不只僅給出了System Call還給出了傳遞的參數和返回值,read讀取的是51號文件句柄,沒有返回成功(unfinished)。

順着這條路,咱們看一下51號文件句柄是什麼

完全解決Tomcat啓動速度慢的問題


/dev/random是Linux下的隨機函數生成器,讀取它至關於生成隨機數字。搜索它,第一個是wiki

完全解決Tomcat啓動速度慢的問題


至此彷佛一切真相大白了,/dev/random會根據噪音產生隨機數,若是噪音不夠它就會阻塞。Linux是經過I/O,鍵盤終端、內存使用量、CPU利用率等方式來收集噪音的,若是噪音不夠生成隨機數的時候就會被阻塞

深刻分析

若是用Tomcat /dev/random做爲關鍵字基本上就可以回答咱們的疑惑了。Tocmat的Session ID是經過SHA1算法計算獲得的,計算Session ID的時候必須有一個密鑰。爲了提升安全性Tomcat在啓動的時候回經過隨機生成一個密鑰。

在 http://wiki.apache.org/tomcat/HowTo/FasterStartUp (Entropy Source部分)有一段解釋。stackoverflow上面也有一大批這方面的說明,因此這裏就再也不多作介紹。

明白了問題的緣由解決起來就很是簡單了——替換/dev/random爲/dev/unrandom,用僞隨機函數生成器(/dev/urandom)來替代隨機函數生成器(/dev/random)。

  • 經過修改Tomcat啓動文件-Djava.security.egd=file:/dev/urandom

  • 經過修改JRE中的java.security文件securerandom.source=file:/dev/urandom

固然JVM的開發者不是傻瓜,Tomcat的開發者也不是二百五。他們之因此沒有選擇/dev/urandom是爲了提升系統的安全性,/dev/urandom並非真正的隨機行爲。(其實通常狀況下/dev/urandom也是足夠安全的不太容易被「重複」)

完全解決問題

上面介紹的兩種方式都是用/dev/urandom替換/dev/random,其實還有第三種方式——增大/dev/random的熵池。問題的緣由是因爲熵池不夠大,因此增大它是最完全的方法。

經過cat /proc/sys/kernel/random/entropy_avail咱們能夠查看如今的熵池大小;咱們須要找到一種方式來提升這個值就好了。若是你的CPU帶有DRNG特性,能夠充分利用硬件來提升熵池產生的速度 。經過cat /proc/cpuinfo | grep rdrand能夠查看本身的CPU是否支持,通常來講Intel的Ivy_Bridge架構的CPU都支持(i三、i5須要注意是否採用該種架構,i7和xeon基本上都支持);AMD的CPU在2015年之後生成的都支持。(若是你是虛擬機須要開啓額外的參數)。若是你的硬件不支持,也沒有關係,咱們可讓/dev/unrandom來作「熵源」。

以Centos7爲例,

  • yum install rngd-tools安裝rngd服務(熵服務)

  • systemctl start rngd啓動服務

  • 若是你的CPU不支持DRNG特性或者像我同樣使用虛擬機,可使用/dev/unrandom來模擬。

  • cp /usr/lib/systemd/system/rngd.service /etc/systemd/system

  • 編輯/etc/systemd/system/rngd.service service小結,ExecStart=/sbin/rngd -f -r /dev/urandom

  • systemctl daemon-reload從新載入服務

  • systemctl restart rngd重啓服務

通過上面的修改,咱們再觀察/proc/sys/kernel/random/entropy_avail基本上在3000左右。咱們能夠測試一下隨機數的生成速度

  • watch -n 1 cat /proc/sys/kernel/random/entropy_avail觀察這個值

  • 新打開一個shell,用dd命令測試隨機數。dd if=/dev/random of=random.dat count=40960

[root@localhost bin]# dd if=/dev/random of=random.dat count=40960
記錄了0+40960 的讀入
記錄了6004+1 的寫出
3074362字節(3.1 MB)已複製,5.01017 秒,614 kB/秒

5秒產生了40960個隨機數,/proc/sys/kernel/random/entropy_avail會有劇烈的變化,全部隨機數產生以後它又會保持在3000左右。

選擇哪一種解決方法

我的建議選擇第三種方式,熵池不只僅Tomcat用,Linux下的全部應用程序產生隨機數都會用到這個,因此不只僅是Tomcat可能被阻塞。若是你搜索會發現Apache、Nginx、OpenSSL都被這個問題坑過。若是咱們經過修改Java的配置來解決這個問題其實只是解決Java應用程序的問題,只能是治標不治本。根治的方法應該是經過rngd提升隨機數生成的速度。

總結

經驗不是經歷。用別人的經驗解決一個問題不難,難的是本身從頭走一遍這條路,更加難的是推翻前人的經驗對一個問題可以有本身的見解和領悟。

這個案例加深了我對strace的理解,對於空中加油這種類型的系統調試有了本身的經驗;經過對緣由的深刻分析我找到了更好的辦法

如何重現故障

能夠很容易的重現文章中描述的故障

  • systemctl stop rngd 中止rngd服務(若是你有啓動rngd)

  • 查看當前熵池的大小cat /proc/sys/kernel/random/entropy_avail

  • head -c1024 /dev/random,強制消費1024個隨機數,系統會長時間沒有反應。直接ctrl+c

  • 再次查看熵池的大小cat /proc/sys/kernel/random/entropy_avail,保證它的大小在儘量的小

  • 啓動tomcat,會發現長時間很長時間的等待

相關文章
相關標籤/搜索