tomcat啟動時非常慢,啟動時 一直卡在Root WebApplicationContext: initialization completed

來源:互聯網
上載者:User

標籤:systemd   隨機函數   call   tool   ace   res   tac   大小   java應用   

每次重啟自己的服務tomcat都需要卡住很長時間,每次都是日誌停在
Root WebApplicationContext: initialization completed in 744 ms這個地方,然後也不知道發生了什麼,在等待
什麼,網上看到了一篇博文,mark下

問題現象

美女同事找我解決一個問題,說Tomcat啟動很慢。開始我以為是程式寫的問題,所以把webapps下所有程式都刪除掉。(只保留Tomcat內建)靈異的事情發生了,Tomcat停止在——


 我查看了一下進程,Tomcat所在的JVM進程已經被啟動了所以可以排除是JVM退出引起的問題。那麼問題真的就是JVM因為某種原因被 阻塞了。
分析

 

問題比較棘手,我排除了CPU、記憶體不足引起的問題;排除了硬碟空間不足引起的問題;我甚至去觀察了網路I/O、硬碟I/O情況,都非常正常。程式被阻塞一般來說一定是要等待某個資源,而現在的情況是所有資源都充足,所以我幾乎想不到是什麼問題引起的。我開始懷疑是KVM Hypervisor虛擬化的問題(用的是虛擬機器)我改變了策略在VMWare開了兩台虛擬機器上直接下載Tomcat啟動。其中一台很快啟動,另一台居然也被阻塞,問題被重現了。眼看要在美女面前丟臉,我光輝偉岸的形象要蕩然無存。這種情況下我不能去“擼”代碼吧?況且Tomcat那麼多人用,真有這麼明顯的Bug早就炸開鍋了。(Tomcat還是很靠譜的不像xxxxStack那麼狗屎)仔細想想我需要找到Tomcat停止在了哪裡?代碼裡發生了什麼事情,但是我又不可能去擼代碼。無可奈何的情況下我決定試一下 strace,這是一個跟蹤系統調用(System Call)的工具,無論是Java還是Pyhton很多資源申請都會變成都會變成System Call。(比如開啟檔案、建立線程、讀寫資料、等待I/O)通過這個工具我至少可以知道Tomcat是停止在哪個System Call上的,這樣可以方便我推斷出問題的原因。
strace -f -o strace.out ./catalina.sh run

strace有很多參數,我用了二個參數
-f 跟蹤fork的子進程,通俗的說會跟蹤所有線程的系統調用

(
-f -- follow forks, -ff -- with output into separate files

)

-o把內容輸出到檔案

其他參數請自行搜尋下面分析strace.out檔案,分析的方法是從下往上(被阻塞的地方肯定是在最後咯)。首先我們需要去掉Tomcat停止引起的System Call,它們不是我們需要的。從後往前搜尋找到SIGINT



紅色部分以上就是引起阻塞的系統調用了,上面有一大堆一大堆的futex
的調用,它是Linux中的一種輕量級的同步方法,所以我們可以判斷出最上面肯定是有某個System Call就是阻塞的真正元兇。跳過所有的futex  這個read
就是引起後面一串futex
的真正原因,strace非常聰明它不僅僅給出了System Call還給出了傳遞的參數和傳回值,read讀取的是51號檔案控制代碼,沒有返回成功(unfinished)。順著這條路,我們看一下51號檔案控制代碼是什麼  /dev/random是Linux下的隨機函數產生器,讀取它相當於產生隨機數字。搜尋它,第一個是wiki 至此似乎一切真相大白了,/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都支援(i3、i5需要注意是否採用該種架構,i7和xeon基本上都支援);AMD的CPU在2015年以後產生的都支援。(如果你是虛擬機器需要開啟額外的參數)。如果你的硬體不支援,也沒有關係,我們可以讓/dev/unrandom來做“熵源”。以Centos7為例,
yum install rngd-tools
或者yum install rng-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

[[email protected] 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,會發現長時間很長時間的等待

還有一個解決方案

在sun的bug database中也已經有人給出,即在java程式啟動參數中添加:-Djava.security.egd=file:/dev/urandom,使用/dev/urandom產生隨機數。

Linux修改catalina.sh檔案

JAVA_OPTS=”-server -Dfile.encoding=UTF-8 -Xms=512m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m -verbose:gc -Xloggc:${CATALINA_HOME}/logs/gc.log`date +%Y-%m-%d-%H-%M` -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -noclassgc”

 

Linux中的隨機數發生器

在Linux作業系統中,有一個特殊的裝置檔案,可以用作隨機數發生器或偽隨機數發生器。

/dev/random

在讀取時,/dev/random裝置會返回小於熵池雜訊總數的隨機位元組。/dev/random可產生高隨機性的公開金鑰或單次密碼本。若熵池空了,對/dev/random的讀操作將會被阻塞,直到從別的裝置中收集到了足夠的環境雜訊為止。

當然你也可以設定成不堵塞,當你在open 的時候設定參數O_NONBLOCK, 但是當你read的時候,如果熵池空了,會返回-1

/dev/urandom

/dev/random的一個副本是/dev/urandom ("unlocked",非阻塞的隨機數發生器[4]),它會重複使用熵池中的資料以產生偽隨機資料。這表示對/dev/urandom的讀取操作不會產生阻塞,但其輸出的熵可能小於/dev/random的。它可以作為產生較低強度密碼的偽隨機數產生器,不建議用於產生高強度長期密碼。

 

 

參考:

https://www.jianshu.com/p/576d356dc163 

tomcat啟動時非常慢,啟動時 一直卡在Root WebApplicationContext: initialization completed

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.