一、背景介紹
經常定位應用容器問題,會遇到以下問題:
-
- 容器突然重啟,為什么會重啟?
- 容器的CPU為什么在不斷的沖高?
- 為什么要給容器分配這么多內存,能不能降低一些?
- 怎么獲取應用的內存、CPU等信息?
遇到以上的問題,對於純業務開發人員,感覺兩眼摸瞎,不知從和下手,一般就是請教前輩,獲取應用的堆棧信息,然后進行分析;但是怎么獲取,怎么分析呢?對於新手或者關注業務比較多的開發人員來說,就只能摸着石頭過河,不僅慢,而且需要走很多彎路。
針對上面問題,考慮讓開發人員能夠快速方便的獲取應用的堆棧信息,並進行分析。以下介紹EMS的不同部署形式下,dump文件的獲取,以及通過一個樣例進行分析。
二、解決思路
為解決上述問題,解決方案如下:
首先,從系統的形式化界面出發,需要解決問題是在項目的某個界面可以通過觸發某個按鈕或者執行什么命令,來實現對應用的堆棧信息dump文件的獲取,並進行應用的堆棧和CPU等進行分析;
然后,從容器的角度出發,通過執行相應的命令,產生對應的dump文件,之后取出文件,進行應用的堆棧和CPU等進行分析;
根據EMS的實際情況,發現兩鍾方式都是可以實現dump文件的獲取,但是由於項目的IAAS和PAAS方式部署,獲取方式有所不同,需要進行不同的實踐;並且目前的項目依賴的JVM是OpenJ9,通過界面獲取會存在一些的問題;
最后,對dump的文件分析過程,依賴JCA工具或者MAT都可以,需要對具體的信息做具體的分析;
三、實踐情況
1.獲取堆棧信息的dump文件
(1) 通過命令終端獲取dump文件
使用kill -3 {Pid} 命令,生成對應應用的dump文件;命令很簡單,主要是如何獲取應用的進程號,並且系統不同的部署方式,獲取的進程號方式不同,以下對不同部署方式下,Pid的獲取:
方式一(IAAS/PAAS部署通用)
- containerName //1. 獲取容器名稱
- containerId=$(docker ps | grep ${containerName} | awk ‘print $1’) //2. 獲取容器id
- Docker inspect ${containerId} |grep Pid //獲取進程號
- Ps -ef | grep ${Pid} //獲取虛機的進程號
- 最后執行kill -3 {虛機進程號},也就是 kill -3 22625,即可以生成堆棧的快照文件。
方式二(進入容器內執行)——前提是容器正常可以進入
- 更直接的方式可以直接進入容器內獲取對應的進程號,並執行kill -3 ${Pid};
方式三(IAAS部署)
- 在MSB上獲取應用的實例端口
- netstat -anp | grep ${port } //獲取進程號
- 最后執行kill -3 進程號 獲取堆棧快照文件
- 分析dump文件信息
通過上面命令獲取的dump文件有兩種格式javacore-dump-***.txt 和 dump-dump-***.phd。
javacore-dump-***.txt 文件: 通常主要是關於應用的cpu使用情況,以及java進程的快照,主要保存的是應用各個線程在某一時刻的運行的位置,即JVM執行的具體位置,包括類方法和對應的行,即threaddump文件。
dump-dump-***.phd : 通常主要是關於應用memory的詳細情況,即對應的heapdump文件,heapdump文件是一個二進制鏡像文件,是某個時刻java堆棧的快照,並且保存JVM堆中對象的使用情況。
四、樣例分析
以下是通過kill -3 ${進程號} 獲取javacore-dump-***.txt文件為例,使用jca工具進行分析(以簡單的線程使用情況分析為例):
1. 首先,查看改文件產生的原因
如下圖,可以看到導致線程快照產生的原因是Dump Event“user”,代表收到SIGQUIT信號(user: SIGQUIT信號,退出信號;gpf:程序一般保護性錯誤導致系統崩潰systhrow:JVM 內部拋出的異常)
2. 然后,可以查看CPU使用情況
從下圖可以看出Msginit線程和kafka 消費線程使用CPU過高,根據對應的代碼流程,可以發現在應用啟動時,會創建一個初始化線程,需要進行本地IO的讀寫,外部接口的調用,需要注冊都是在該線程進行的處理,需要對其進行優化;kafka消費線程一直保持着比較高的CPU的占用率,主要原因是一個線程注冊的主題太多導致;
3. 根據上面的兩個線程CPU使用過高,查詢兩個線程的堆棧信息
首先,查看MsgInit線程的堆棧信息,發現從CPU初步分析的結果只是大略的確定線程的問題是本地IO讀寫,注冊等原因,從下圖的堆棧可以發現,原因是kafka的注冊消費導致的,線程注冊kafka后一直在拉取信息進行處理;
然后,從kafka消費線程可以看出消費的消息過多和注冊的主題過多導致;
最后,確定都與kafka的消費有關系;
4. 最后,可以確定性能瓶頸出現在kafka消費過程中,可以增加線程對消費過程進行處理。
五、后續思考
- 當前的樣例分析很簡單,僅是對突出的兩個線程使用情況進行分析,沒有充分利用dump文件中的其他信息,其中還有一些內存使用情況分析、其他線程堆棧分析,以及線程的比較分析和監控器等。可以發現對JVM使用情況的分析不同的場景以及不同的應用關注點不同,需要分析的內容不同,線程、內存情況、堆棧情況或者堆中的對象使用情況等等各不相同。
- 對dump文件的獲取盡量向界面化發展,讓分析人員可以在應用界面就可以獲取文件進行分析;
- 應總結多種場景的dump文件,並輸出不同的樣例,以便開發人員能夠根據不同場景快速的獲取分析流程,以及做出相應的處理;
六、相關鏈接
l https://www.ibm.com/support/pages/ibm-thread-and-monitor-dump-analyzer-java-tmda
l https://www.cnblogs.com/mu-tou-man/p/5619354.html