Story backgroundjava
回望2018年12月,這也許是程序員們日夜不得安寧的日子,皆因各類前線的系統使用者都須要衝業績等緣由,每每在這個時候會向系統同時寫入海量的數據,當咱們的應用或者數據庫服務器反應不過來的時候,就會產生各類各樣詭異的問題,諸如表現出來就是系統變得巨卡無比,沒法使用,或者週期性卡頓,使人髮指,用戶輕則問候系統全家,重則心臟病發。總而言之天天都腦袋疼!歸根究竟是咱們的應用服務器或數據庫服務器由於扛不住流量形成的系統BUG問題暴露,諸如OOM等,呈現出機器的三高,這裏說的三高並非高血脂、高血壓、高血糖。而是高CPU,高內存,高NETWORK/IO!(本文只講述應用服務器,暫不講述數據庫服務器形成的問題)PS:爲何不作壓測?石麗不容許呀程序員
Begin Transaction
數據庫
而小魯班剛好是參與這個問題系統的開發者之一,這天小魯班起得比較晚,到了公司都快10點了,不過沒所謂,最近都沒什麼新需求要作,小魯班打開電腦正準備一天的摸魚旅途。突然之間門口傳來一陣急促的腳步聲,小魯班回眸一看,噢原來是項目經理提着大刀殺來了,詢問之原來前線系統又掛了,這些日子裏看到系統每天被投訴,其實也義不容辭,小魯班內心想系統掛了不是運維的事情麼,別魯班抓老鼠了!不過內心又想TM技術運維好像刪庫跑路了。沒辦法,小魯班臨危受命硬着頭皮上吧。windows
回顧一下問題系統的架構,是典型的應用集羣服務器。理論上來講,有作應用集羣的話,只要有一個應用服務器沒死,一個數據庫服務器沒死,系統仍是能夠正常運行的,只是處理請求的平均速度有所下降。無論怎麼說,只要有一臺應用或數據庫掛了,基本上都是由於代碼存在bug,致使三高,使得機器沒法發揮其原本的性能,若是未能及時發現處理,其餘集羣的服務器機器也很快被同一個BUG拖垮,例如死鎖,最終致使沒有一臺機器能提供服務,集羣系統最終走向癱瘓,換句通俗的話來講就是系統bug的存在使得機器生活不能自理,因此更不能向外提供服務。api
Try
服務器
小魯班儘管是科班出生的,但對於此類系統線上問題,一時之間還真摸不着頭腦,無奈之下只好請教小魯班的表哥魯班大師,要知道,魯班大師但是有着一套教科書式的解決方案,而且能夠經過一些途徑來定位並解決系統bug,特別是代碼緣由產生的問題,從根本上杜絕問題,而不是每次出現問題都重啓應用服務器就算了,由於知道bug一天不除,系統永遠均可能會隨時隨地go die。架構
因而小魯班立馬拿起大哥大手機向表哥發了個紅包,沒想到這TM就把他表哥魯班大師炸了出來,並取得了併發
解決方案三部曲運維
1.查看應用服務器指標(CPU,內存,NETWORK,IO),並保留現場全部線索日誌,而後進行問題歸類和分析,最後重啓或回滾應用,以達到先恢復服務,後解決BUG的目的工具
2.經過分析排查工具或命令對1步驟中取得的線索日誌進行推測分析,或許能定位到bug代碼部分,或許能發現JVM某些參數不合理
3.修改bug代碼或調節JVM參數,達到修復bug的目的,晚上從新發布應用
不妨畫一張圖
那麼具體是怎麼操做的呢
1.首先固然是查看機器的指標啦,Linux則是使用TOP和JPS命令列出進程以及各個進程所佔用的CPU和內存使用狀況。而windows則是直接打開任務管理器就能夠看到,但不管什麼操做系統的服務器,咱們均可以經過JConsole這個JDK自帶的可視化工具遠程鏈接來看到內存的使用趨勢,這樣一下來,咱們就能大體肯定是否由於內存出現問題了,例以下圖出現的CPU滿的問題,可是咱們沒法知道是什麼緣由致使的問題。
2.要知道是什麼緣由致使的,咱們得要知道目前JVM是用什麼垃圾回收器,畢竟有些回收器是CPU敏感有些則不是,經過命令-XX:+PrintFlagsFinal 打印JVM全部參數
或者-XX:+PrintCommandLineFlags經過打印被修改過的參數均可以知道,這個能夠幫助咱們分析是發生內存問題的緣由。
3.而後咱們得打印GC日誌,經過日誌分析,其實能夠發現不少線索,好比經過GC發生的次數,來判斷堆的大小是不是合理的範圍,經過發生GC後,回收的對象容量多少來判斷是否存在內存泄漏嫌疑,特別是FGC,系統卡頓是否與STW有關等等
那麼日誌怎麼看呢,如圖吧
4.假如發生了內存問題,這個時候須要經過打印內存使用量來定位問題點Jstar
實時查看gc的情況,經過jstat -gcutil 能夠查看new區、old區等堆空間的內存使用詳情,以及gc發生的次數和時間
執行:cd $JAVA_HOME/bin中執行jstat,注意jstat後必定要跟參數。具體命令行:jstat -gcutil 'java進程PID' 1000
如圖所示,發現Eden區、Old區都滿了,此時新建立的對象沒法存放,晉升到Old區的對象也沒法存放,因此係統持續出現Full GC了。
5.若是看不到哪裏出問題了,須要把內存鏡像導出,而後用專業的工具去分享鏡像如MAT
固然了,咱們也能夠設置當發生OOM的時候,自動導出進程鏡像
把DUMP文件導入MAT會很是直觀的展示內存泄漏的可疑點,能夠直接看到某個線程佔用了大量的內存,找到某個對象,常見的都是集合對象被GC ROOT指向等一直不能被回收
catch
固然以上是內存出現的問題,可是若是是CUP高出現的問題,會比較好搞一些,由於線程是運行在CPU上的,而線程卻能夠經過分析棧信息獲取,而咱們寫的方法不也是運行到棧上的嗎
可以使用top -H查看線程的cpu消耗情況,這裏有可能會看到有個別線程是cpu消耗的主體,這種狀況一般會比較好解決,可根據top看到的線程id進行十六進制的轉換,用轉換出來的值和jstack出來的java線程堆棧的nid=0x[十六進制的線程id]進行關聯,便可看到此線程到底在作什麼動做,這個時候須要進一步的去排查究竟是什麼緣由形成的,例若有多是正則計算,有多是很深的遞歸或循環,也有多是錯誤的在併發場景使用HashMap等。
1.找到最耗CPU資源的java線程TID執行命令:ps H -eo user,pid,ppid,tid,time,%cpu,cmd | grep java | sort -k6 -nr |head -1
2.將找到的線程TID進行十六進制的轉換
3.經過命令導出堆棧文件:jstack javapid > jstack.log,根據上一步獲得的線程TID,搜索堆棧文件,便可看到此線程到底在作什麼動做。如對像鎖互鎖等,業務邏輯存在計算的死循環等
咱們能夠獲得相似一張這樣的圖,百度找的,能夠定位到某個方法是否存在邏輯漏洞
固然還能夠用可視化工具jvisualvm,直接監控進程和線程,而且能夠自動分析出一些明顯的BUG
End Transaction
小結
系統忽然掛了,怎麼排查問題,首先得肯定是內存出了問題,那出現內存問題該怎樣排查,對於這個問題,常常會聽到一些不全面的作法,那就是隻經過一種方式來查問題。例如只經過Dump文件來查,或者只經過 Jconsole來查等等其實回答都對,但都不全面。例如,醫生在診斷病情時,不少情況下不會只相據B超單或驗血單來下結論,他們每每會根據多張化驗結果來綜合判斷。診斷內存問題也應該這樣。下面給出了各類方法的擅長點和同限性。
最後總結一波:
1.經過JConose確認發生內存問題
2.在寫有可能致使內存問題的代碼時候,記錄日誌,並打印當前內存使用量,用這些信息和GC日誌來綜合判斷
3.經過Dump文件重現OOM的內存鏡像 | 若是是CPU問題,能夠考慮分析線程DUMP
4.找到問題代碼修改