自己再阿里云申請了一臺1G1核的機器,每次重啟自己的服務(wù)tomcat都需要卡住很長時間擂红,每次都是日志停在
Root WebApplicationContext: initialization completed in 744 ms這個地方,然后也不知道發(fā)生了什么围小,在等待
什么昵骤,網(wǎng)上看到了一篇博文树碱,mark下
問題現(xiàn)象
美女同事找我解決一個問題,說Tomcat啟動很慢变秦。開始我以為是程序?qū)懙膯栴}成榜,所以把webapps下所有程序都刪除掉。(只保留Tomcat自帶)靈異的事情發(fā)生了蹦玫,Tomcat停止在——
分析
問題比較棘手,我排除了CPU额港、內(nèi)存不足引起的問題饺窿;排除了硬盤空間不足引起的問題;我甚至去觀察了網(wǎng)絡(luò)I/O移斩、硬盤I/O情況肚医,都非常正常。程序被阻塞一般來說一定是要等待某個資源向瓷,而現(xiàn)在的情況是所有資源都充足肠套,所以我?guī)缀跸氩坏绞鞘裁磫栴}引起的。我開始懷疑是KVM Hypervisor虛擬化的問題(用的是虛擬機)我改變了策略在VMWare開了兩臺虛擬機上直接下載Tomcat啟動猖任。其中一臺很快啟動你稚,另一臺居然也被阻塞,問題被重現(xiàn)了朱躺。眼看要在美女面前丟臉刁赖,我光輝偉岸的形象要蕩然無存。這種情況下我不能去“擼”代碼吧长搀?況且Tomcat那么多人用宇弛,真有這么明顯的Bug早就炸開鍋了。(Tomcat還是很靠譜的不像xxxxStack那么狗屎)仔細想想我需要找到Tomcat停止在了哪里源请?代碼里發(fā)生了什么事情枪芒,但是我又不可能去擼代碼。無可奈何的情況下我決定試一下 strace谁尸,這是一個跟蹤系統(tǒng)調(diào)用(System Call)的工具舅踪,無論是Java還是Pyhton很多資源申請都會變成都會變成System Call。(比如打開文件良蛮、新建線程硫朦、讀寫數(shù)據(jù)、等待I/O)通過這個工具我至少可以知道Tomcat是停止在哪個System Call上的背镇,這樣可以方便我推斷出問題的原因咬展。
strace -f -o strace.out ./catalina.sh run
strace有很多參數(shù)泽裳,我用了二個參數(shù)
-f 跟蹤fork的子進程,通俗的說會跟蹤所有線程的系統(tǒng)調(diào)用
-o把內(nèi)容輸出到文件
其他參數(shù)請自行搜索下面分析strace.out文件破婆,分析的方法是從下往上(被阻塞的地方肯定是在最后咯)涮总。首先我們需要去掉Tomcat停止引起的System Call,它們不是我們需要的祷舀。從后往前搜索找到SIGINT
的調(diào)用,它是Linux中的一種輕量級的同步方法裳扯,所以我們可以判斷出最上面肯定是有某個System Call就是阻塞的真正元兇抛丽。跳過所有的futex
:
就是引起后面一串futex
的真正原因,strace非常聰明它不僅僅給出了System Call還給出了傳遞的參數(shù)和返回值饰豺,read讀取的是51號文件句柄亿鲜,沒有返回成功(unfinished)。順著這條路冤吨,我們看一下51號文件句柄是什么
深入分析
如果用Tomcat /dev/random作為關(guān)鍵字基本上就能夠回答我們的疑惑了翘簇。Tocmat的Session ID是通過SHA1算法計算得到的撬码,計算Session ID的時候必須有一個密鑰。為了提高安全性Tomcat在啟動的時候回通過隨機生成一個密鑰版保。在 http://wiki.apache.org/tomcat/HowTo/FasterStartUp (Entropy Source部分)有一段解釋呜笑。stackoverflow上面也有一大批這方面的說明,所以這里就不再多做介紹彻犁。明白了問題的原因解決起來就非常簡單了——替換/dev/random為/dev/unrandom叫胁,用偽隨機函數(shù)生成器(/dev/urandom)來替代隨機函數(shù)生成器(/dev/random)。
通過修改Tomcat啟動文件-Djava.security.egd=file:/dev/urandom
通過修改JRE中的java.security文件securerandom.source=file:/dev/urandom
當然JVM的開發(fā)者不是傻瓜汞幢,Tomcat的開發(fā)者也不是二百五驼鹅。他們之所以沒有選擇/dev/urandom是為了提高系統(tǒng)的安全性,/dev/urandom并不是真正的隨機行為。(其實一般情況下/dev/urandom也是足夠安全的不太容易被“重復”)
徹底解決問題
上面介紹的兩種方式都是用/dev/urandom替換/dev/random输钩,其實還有第三種方式——增大/dev/random的熵池豺型。問題的原因是由于熵池不夠大,所以增大它是最徹底的方法买乃。通過cat /proc/sys/kernel/random/entropy_avail
我們可以查看現(xiàn)在的熵池大幸霭薄;我們需要找到一種方式來提高這個值就行了剪验。如果你的CPU帶有DRNG特性肴焊,可以充分利用硬件來提高熵池產(chǎn)生的速度 。通過cat /proc/cpuinfo | grep rdrand
可以查看自己的CPU是否支持功戚,一般來說Intel的Ivy_Bridge架構(gòu)的CPU都支持(i3娶眷、i5需要注意是否采用該種架構(gòu),i7和xeon基本上都支持)啸臀;AMD的CPU在2015年以后生成的都支持届宠。(如果你是虛擬機需要開啟額外的參數(shù))。如果你的硬件不支持壳咕,也沒有關(guān)系,我們可以讓/dev/unrandom來做“熵源”顽馋。以Centos7為例谓厘,
yum install rngd-tools
或者yum install rng-tools
安裝rngd服務(wù)(熵服務(wù))
systemctl start rngd
啟動服務(wù)
如果你的CPU不支持DRNG特性或者像我一樣使用虛擬機,可以使用/dev/unrandom來模擬寸谜。
cp /usr/lib/systemd/system/rngd.service /etc/systemd/system
編輯/etc/systemd/system/rngd.service
service小結(jié)竟稳,ExecStart=/sbin/rngd -f -r /dev/urandom
systemctl daemon-reload
重新載入服務(wù)
systemctl restart rngd
重啟服務(wù)
經(jīng)過上面的修改,我們再觀察/proc/sys/kernel/random/entropy_avail
基本上在3000左右熊痴。我們可以測試一下隨機數(shù)的生成速度
watch -n 1 cat /proc/sys/kernel/random/entropy_avail
觀察這個值
新打開一個shell他爸,用dd命令測試隨機數(shù)。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字節(jié)(3.1 MB)已復制果善,5.01017 秒诊笤,614 kB/秒
5秒產(chǎn)生了40960個隨機數(shù),/proc/sys/kernel/random/entropy_avail會有劇烈的變化巾陕,所有隨機數(shù)產(chǎn)生之后它又會保持在3000左右讨跟。
選擇哪種解決方法
個人建議選擇第三種方式,熵池不僅僅Tomcat用鄙煤,Linux下的所有應(yīng)用程序產(chǎn)生隨機數(shù)都會用到這個晾匠,所以不僅僅是Tomcat可能被阻塞。如果你搜索會發(fā)現(xiàn)Apache梯刚、Nginx凉馆、OpenSSL都被這個問題坑過。如果我們通過修改Java的配置來解決這個問題其實只是解決Java應(yīng)用程序的問題,只能是治標不治本澜共。根治的方法應(yīng)該是通過rngd
提高隨機數(shù)生成的速度向叉。
總結(jié)
經(jīng)驗不是經(jīng)歷。用別人的經(jīng)驗解決一個問題不難咳胃,難的是自己從頭走一遍這條路植康,更加難的是推翻前人的經(jīng)驗對一個問題能夠有自己的看法和領(lǐng)悟。這個案例加深了我對strace
的理解展懈,對于空中加油
這種類型的系統(tǒng)調(diào)試有了自己的經(jīng)驗销睁;通過對原因的深入分析我找到了更好的辦法。這就是康德精神——思考存崖、批判冻记、理性。
如何重現(xiàn)故障
可以很容易的重現(xiàn)文章中描述的故障
systemctl stop rngd
停止rngd服務(wù)(如果你有啟動rngd)
查看當前熵池的大小cat /proc/sys/kernel/random/entropy_avail
head -c1024 /dev/random
来惧,強制消費1024個隨機數(shù)冗栗,系統(tǒng)會長時間沒有反應(yīng)。直接ctrl+c
再次查看熵池的大小cat /proc/sys/kernel/random/entropy_avail
供搀,保證它的大小在盡可能的小
啟動tomcat隅居,會發(fā)現(xiàn)長時間很長時間的等待
還有一個解決方案
在sun的bug database中也已經(jīng)有人給出,即在java程序啟動參數(shù)中添加:-Djava.security.egd=file:/dev/urandom葛虐,使用/dev/urandom生成隨機數(shù)胎源。