原文地址:http://m.blog.csdn.net/article/details?id=43376943
前言:我們的場景並沒有像BAT等大型互聯網公司里的系統那么復雜,但是基本上也有一定的規模,暫且就認為我們是一個中型互聯網系統。但是工具和思路都是差不多的,因為原理是一樣的。同時下面提到的命令,具體詳細的用法,我想google上已經有足夠多的資源了,這里只說思路與方向。
首先我們分析一下服務器構造和操作系統,服務器我們可以簡單的分解為CPU、內存、硬盤(其他部件的我們就不考慮了),操作系統:我們簡單的認為操作系統是來協同上面說的CPU、內存、硬盤的一個系統,然而jdk bin目錄里的應用程序最后都會運行在操作系統上,所以jdk會借助操作系統來操作CPU、內存、硬盤。 所以常見的線上問題,通常就出現在CPU、內存、硬盤和操作系統這個幾個維度上。
這里總結一下我所碰到過的問題:CPU 利用率高、內存溢出、應用程序很卡(但是又沒掛)、各種詭異問題(很難重現但總是偶爾出現)等。
首先我們來說內存,因為通常內存問題是最經常出的,常見的有內存溢出(OOM)、GC問題等。排查這些問題需要了解linux相關命令:
-
top 后,shift + m 來對各進程按內存進行排序
-
free 查看linux整體內存情況
OOM異常最好排查,因為已經溢出報錯了,常用解決辦法:
-
jmap 導出內存堆棧信息,並用第三方工具(比如MAT)分析,這些工具基本上可以展示到底是哪種對象最多。(這種是最常用的方法)
-
第三方工具在線展現具體的堆棧信息,比如:https://github.com/alibaba/TBJMap (我並沒有實際用過,但是淘寶開源的一定是他們實際用過的,但是一般此類工具最好還是在測試環境中使用)
-
jprofiler、jvisualvm(具體怎么用,請google)
上面的幾種工具和思路,已經基本能搞定OOM類型的問題了
GC問題:我們所碰到過的通常會因為GC回收過於頻繁導致CPU過高,或者因為fullgc讓整個java進程暫停。此類問題可以借助 jstat -gcutil pid查看各個區域和gc相關信息。當然一些在線工具(jprofiler、jvisualvm)也可以查看,但是不推薦線上使用,jstat已經很NB了。至於里面具體每個字段什么意思,google上已經非常多了。通常查jstat + jmap分析已經基本上能讓你知道,是哪個地方的內存問題引起了GC問題。
但是,jstat上會出來很多信息,這里需要對java的內存管理有一定的了解,至少得知道,堆棧、分代機制、新生代、老生代等相關區域有一定的了解。總之要解決內存問題,得對java里的內存要了解。
CPU問題會相對來說復雜很多,CPU好比服務器的大腦,很多其他的組件也可能會導致CPU利用率過高。總結一下我們碰到過的有以下幾中:
-
的確計算量過大,cpu運算不過來
-
IO讀寫過於頻繁
-
java線程競爭鎖過於頻繁
-
應用程序很卡(但是又沒掛)
要排查CPU相關的問題,首先得了解linux的top命令,top的功能非常多,具體每個屬性什么意思請先google一下。
解決CPU類的問題總體思路:找到java進程中哪些線程使用CPU很高,簡單的說就是top -H -p pid,這個命令會出現java進程里哪些線程使用CPU很高,會按降序排序,但是子線程編號與jstack pid 里出來的不太一樣,jstack里出來的是16進程,所以把top -H -p里的轉成16進程,然后查找一下具體的線程。
1.的確計算量過大,cpu運算不過來
通過上面的top -H -p和 jstack基本已經能看出來這種類型的問題
2.IO讀寫過於頻繁
截圖中的wa。這個代表是cpu等待io的時間,簡單的說就是io正在處理,告訴cpu請等我一下,我處理完了就讓你去干別的。因為cpu是固定資源,它被io所持有,那么別人就沒有辦法使用,所以就導致cpu使用率上去了。所以io wait很高就思路很清楚了,接下來就要去查看具體哪些線程在頻繁操作io。
接下來就用jstack pid 來查看具體jdk中具體線程,先掃一眼,看是否有很多線程都處於io操作的階段。但是這還不能完全解決問題,有的時候信息太多,實在不好找,一眼眼看,要看得頭痛。所以簡單點也可以使用第1點里提到的方法。
3.java線程競爭鎖過於頻繁
top -H -p和 jstack已經基本上能看出,但是這里還需要對java 線程的各種狀態要了解一下。要能理解jstack出來的線程信息里每種狀態是什么含義。這樣才能分析出具體原因,不然跑去看代碼總感覺不應該啊,應該沒錯啊。
4.應用程序很卡(但是又沒掛)
這類問題如果業務不是很復雜,基本上用top -H -p 和 jstack可以查看得出來。但是有的時候還不能完全確定,一種常見的辦法就是打日志,這種辦法當然是OK的。但是這里再介紹一些可以直接在線上就能定位和分析的工具。https://github.com/alibaba/TProfiler,號稱是可以線上使用的分析工具,淘寶開源的東西我們已經使用過很多了,基本上是靠譜的。
各種詭異問題:
所謂詭異問題,通常是飄忽不定,偶然出現,又難重現,搞得人非常想砸電腦的問題。但是它又的確出現了,我一直強調這個世界是科學的,碰到jvm編譯器級別的BUG,我想有,但是可能不是你的這次案例,所以不要輕易懷疑是jvm的問題,我想不是像BAT等一些非常大型的系統很難碰到編譯器級別的問題。
那么碰到這種問題不好重現,一種就是打日志,通過日志來排查程序運行的邏輯,比如一些多線程引起的邏輯問題。如果打日志太麻煩,而且又要重啟很多東西,那么就可能要搬出神器來了,這里介紹兩種(雖然兩種都沒有線上用來解決過實際問題,但按原理介紹應該是種解決問題的思路,畢竟不可能每種工具用過,了其原理就可以來判斷是否能為己所用),第一種是BTrace、第二種是HouseMD(號稱比BTrace更好用的工具)https://github.com/CSUG/HouseMD/wiki/UserGuideCN,阿里系也在使用,我想應該是靠譜的。他們的原理就是在線上運行的代碼上動態的編織一些“料”進去,並能抽取其中一些結果來給調試者提供信息。
不過我的實踐經驗通常詭異問題是因為一些小細節的疏忽,或者一些低級錯誤。然后找到恨不得抽自己三個嘴巴。所以對原理的理解才能寫出更好的代碼。
其他碰到過的問題:
-
因為linux文件系統權限的問題,導致某些框架沒有讀權限,拋異常,而此類異常有的時候不是直接提示沒有權限,比如有的可能會提示空指針異常,而這類問題去google通常是沒有答案的。如果是空指針,那就好辦了,去看源碼。我想已經沒有比空指針還好解決的問題了。
接下來我們總結一下,我們上面提到了下面這些關鍵詞:
-
top
-
top -H -p (這個很有用)
-
jmap
-
tbjmap
-
jstack
-
jprofiler
-
jvisualvm
-
TProfiler
-
BTrace
-
HouseMD
-
java 線程狀態
-
java 內存管理
每一種類型的都用過一次已經不錯了,每一種類型的都在線上真實的排查過用過,那就更不錯了,不過線上有這么多的問題也夠嚴重的了。如果每一種工具和類型的問題都對其背后的工具和原理實踐過、學習過,我想功力已經非常不錯了。所以碰到每一個線上問題,都要有能夠發散的思路,並能深入下去。而不是停留在工具這個層面。
工具上面已經介紹很多了,但是真正的場景中不可能每個去試,等試完所有的已經不叫快速了,通常做一個系統,會大致了解這個系統是IO密集型還是計算密集型應用,或者在開發的過程就已經知道了未來可能哪些地方會有增長。
通常出問題了,先上去看log4j和tomcat的日志,如果日志里能體現問題在哪里基本就到這里了,如果日志正常。那么通常是應用比較慢,那么先top。如果機器load高,找到CPU占用最高的進程。如果是java進程,可以先jstack 看個大概,如果還沒有頭緒, 就同時看一下GC情況,這時就要用jstat,gc正常,那就再用top -H -p,找CPU最高的地方。這么一套流程跑下來基本上能定位問題。如果load不高,那么情況就要復雜很多,要檢查一些外圍組件。
1、DB,看是不是很多SQL積壓。如果sql也正常,別的表象都正常,那么就要利用類似TProfiler類似的工具(前提是你已經預先就部署好了)。
2、http,linux連接數是否很多,當前網絡流量是不是跑滿了,甚至我還碰到過某個http api所對應的域名dns有問題 ping 一下要幾百ms。(淘寶還有網卡跑滿的情況。。)
3、第三方本地應用慢,有的時候會使用一些linux上的工具,比如圖片壓縮。這些應也可能會影響程序。
上面的是我的常用思路和方法,但是具體的情況會有變化,比如我們現在在用的是阿里雲的ECS,一台機器只有5M,所以帶寬我會優先觀察,如果帶寬正常再看面的,所以具體的思路和方法還是要看對系統和當時的環境有多了解,需要做一些調整。
那讓我們繼續帶着問題發散下去,接下來我們准備一些問題。
1、通常jstack 后 出來的線程堆棧信息后,再去top -H -p(或者反過來也可以)。中間總有時間差,可能jstack里的那個線程與top -H -p中出來的 那個已經對不上了。那這樣可能並不能真實的反應實際情況。(當然通常是OK的)。那么有沒有辦法可以快速的來一個命令就能出來對應的線程?當然沒有,那么沒有就自己想原理能不能寫嘛。這里應該有很多種辦法可以做,比如可以通過shell把幾個命令組合在一起,然后抽取對應的信息,然后分析,如果shell太復雜了,也可以用perl、python甚至java寫一個linux上的小程序也是OK的啊。這樣執行一個命令就可以快速的得到所有信息。再發散下去,有的時候不是每台機器都要登錄上去,那能不能在某台特定的機器,甚至在自己的電腦上就可以去查看呢?是否可以做成網頁,直接在瀏覽器上查看?
2、當出現問題后我們上去機器查看具體問題,我們認為自己是在拉信息,那么是否可以改成推模式?快有問題時推送數據到某個地方,說俗點就是監控和預警,那么我們使用的tomcat、jetty等容器是否可以在線監控?是否有預留接口?那要收集所有機器上的信息到同一個地方具體又有哪些解決方案。方案有一萬種,全在google和基礎原理中。
3、如果要做監控,那么怎么快速又靈活的添加監控項目,同時監控又不過於影響和侵入線上的業務,中間又有很多問題需要解決。
我想如果應用不是非常復雜,上面的這些方法和思路,基本上已經能解決絕大多數的問題了。
最后,我想線上問題最好的情況應該是被監控發現,就算要上去調試,用工具會比人手快,比如jstack 和 top -H -p用腳本去自動抽取top N最消耗CPU的子線程。