"服務(wù)不可用"怎么排查?
點(diǎn)擊上方[全棧開發(fā)者社區(qū)]→右上角[...]→[設(shè)為星標(biāo)?]

一、發(fā)現(xiàn)問題
下面是線上機(jī)器的cpu使用率,可以看到從4月8日開始,隨著時間cpu使用率在逐步增高,最終使用率達(dá)到100%導(dǎo)致線上服務(wù)不可用,后面重啟了機(jī)器后恢復(fù)。

二、排查思路
簡單分析下可能出問題的地方,分為5個方向:
1.系統(tǒng)本身代碼問題
2.內(nèi)部下游系統(tǒng)的問題導(dǎo)致的雪崩效應(yīng)
3.上游系統(tǒng)調(diào)用量突增
4.http請求第三方的問題
5.機(jī)器本身的問題
三、開始排查
1.查看日志,沒有發(fā)現(xiàn)集中的錯誤日志,初步排除代碼邏輯處理錯誤。
2.首先聯(lián)系了內(nèi)部下游系統(tǒng)觀察了他們的監(jiān)控,發(fā)現(xiàn)一起正常。可以排除下游系統(tǒng)故障對我們的影響。
3.查看provider接口的調(diào)用量,對比7天沒有突增,排除業(yè)務(wù)方調(diào)用量的問題。
4.查看tcp監(jiān)控,TCP狀態(tài)正常,可以排除是http請求第三方超時帶來的問題。
5.查看機(jī)器監(jiān)控,6臺機(jī)器cpu都在上升,每個機(jī)器情況一樣。排除機(jī)器故障問題。
即通過上述方法沒有直接定位到問題。
四、解決方案
1.重啟了6臺中問題比較嚴(yán)重的5臺機(jī)器,先恢復(fù)業(yè)務(wù)。保留一臺現(xiàn)場,用來分析問題。
2.查看當(dāng)前的tomcat線程pid

3.查看該pid下線程對應(yīng)的系統(tǒng)占用情況。top -Hp 384

4.發(fā)現(xiàn)pid 4430 4431 4432 4433 線程分別占用了約40%的cpu
5.將這幾個pid轉(zhuǎn)為16進(jìn)制,分別為114e 114f 1150 1151
6.下載當(dāng)前的java線程棧 sudo -u tomcat jstack -l 384>/1.txt
7.查詢5中對應(yīng)的線程情況,發(fā)現(xiàn)都是gc線程導(dǎo)致的

8.dump java堆數(shù)據(jù)
sudo -u tomcat jmap -dump:live,format=b,file=/dump201612271310.dat 384
9.使用MAT加載堆文件,可以看到j(luò)avax.crypto.JceSecurity對象占用了95%的內(nèi)存空間,初步定位到問題。
MAT下載地址:http://www.eclipse.org/mat/


10.查看類的引用樹,看到BouncyCastleProvider對象持有過多。即我們代碼中對該對象的處理方式是錯誤的,定位到問題。
五、代碼分析
我們代碼中有一塊是這樣寫的

這是加解密的功能,每次運(yùn)行加解密都會new一個BouncyCastleProvider對象,放倒Cipher.getInstance()方法中。
看下Cipher.getInstance()的實現(xiàn),這是jdk的底層代碼實現(xiàn),追蹤到JceSecurity類中

verifyingProviders每次put后都會remove,verificationResults只會put,不會remove.

看到verificationResults是一個static的map,即屬于JceSecurity類的。
所以每次運(yùn)行到加解密都會向這個map put一個對象,而這個map屬于類的維度,所以不會被GC回收。這就導(dǎo)致了大量的new的對象不被回收。
六、代碼改進(jìn)
將有問題的對象置為static,每個類持有一個,不會多次新建。

七、本文總結(jié)
遇到線上問題不要慌,首先確認(rèn)排查問題的思路:
查看日志 查看CPU情況 查看TCP情況 查看java線程,jstack 查看java堆,jmap 通過MAT分析堆文件,尋找無法被回收的對象
作者 |?kingszelda
覺得本文對你有幫助?請分享給更多人
關(guān)注「全棧開發(fā)者社區(qū)」加星標(biāo),提升全棧技能
本公眾號會不定期給大家發(fā)福利,包括送書、學(xué)習(xí)資源等,敬請期待吧!
如果感覺推送內(nèi)容不錯,不妨右下角點(diǎn)個在看轉(zhuǎn)發(fā)朋友圈或收藏,感謝支持。
好文章,留言、點(diǎn)贊、在看和分享一條龍吧??
