Java診斷利器Arthas優雅排查生產環境

2019-11-06     程式設計師聖經

來源公眾號ytao ,

作者ytao

前言

Arthas 是Alibaba開源的Java診斷工具。在線排查問題,無需重啟;動態跟蹤Java代碼;實時監控JVM狀態。對分秒必爭的線上異常, Arthas可幫助我們快速診斷相關問題。

下載安裝

下載 Arthas的 arthas-boot.jar

wget https://alibaba.github.io/arthas/arthas-boot.jar

下載 arthas之後,先來了解幫助信息,可以通過 java-jar arthas-boot.jar-h命令查看,這裡給出了一些例子和參數說明

啟動

啟動 arthas之前,先啟動一個 springboot的應用。該 demo在地址https://github.com/yangtao9502/ytao-springboot-demo

java -jar ytao-springboot-demo.jar

啟動 arthas-boot.jar命令

java -jar arthas-boot.jar

這裡注意需要啟動 demo和 arthas使用同一權限用戶,否則使用attach機制獲取不到進程信息(這裡剛使用時沒注意,遇到過這個問題)。例:root用戶啟動 demo, u1用戶啟動 arthas時,列印信息 Cannotfind java process.Trytopassincommand line.

查看源碼,在獲取進程之後,添加日誌輸出。結果為空,返回 -1,判斷結果小於 0時,直接退出。

啟動類 Bootstrap#main的代碼

進程工具類 ProcessUtils#select的代碼

通過上面也分析到,我們啟動 arthas之前,必須要先啟動我們的目標進程,否則 arthas可能無法啟動。

使用 root用戶啟動成功介面

選擇java進程,這裡我們的 ytao-springboot-demo是 1,選擇後會有連接信息

dashboard 數據面板

使用 dashboard命令,可以查看線程,內存,GC,以及Runtime信息

jad 反編譯

有時我們會遇到線上代碼運行結果不是我們期望的結果,有種情況就是線上代碼不是我們想要的版本,但是要查看的話,需要下載後再進行反編譯。這時 arthas的 jad可以幫助我們線上進行即時反編譯,確認代碼是否符合我們的版本。

jad com.ytao.service.UserServiceImpl

watch 函數執行信息

使用 watch命令可以查看函數的執行信息。watch的參數列表(來自官網)

當我們遇到線上數據 bug時,我們一般處理的手段就是開發環境模擬線上數據,從生產日誌中查找線索,再或者遠程 debug。以上不管哪種排查手段,相對都是比較麻煩。這時Arthas的 watch可以幫助我們查看實時的代碼執行情況。使用觀察表達式可以查看函數的 參數, 返回值, 異常信息。觀察表達式主要由 OGNL表達式組成,所以可以編寫 OGNL表達式來執行。

觀察表達式的變量

查看一個函數的入參和返回值

watch com.ytao.service.UserServiceImpl getUser "{params,returnObj}"

列印信息 isEmpty=false;size=1可以看到參數為非空,參數數量為一個。查看具體入參信息

watch com.ytao.service.UserServiceImpl getUser "{params[0],returnObj}"

查看異常信息

watch com.ytao.service.UserServiceImpl getUser "throwExp"

當我們傳入一個參數為 -1時,列印出我們定義的非法參數異常

watch除了觀察表達式外,還能使用 條件表達式,以及 觀察事件點。注意使用觀察事件點時,有些觀察表達式的變量不一定存在,比如使用 -b時,返回值和異常信息都為空。

有時我們排查某個函數,不能馬上獲取到函數的信息, arthas給提供的 後台異步任務可以幫助我們記錄日誌。使用方式和Linux的類似。

watch com.ytao.service.UserServiceImpl getUser "{params,returnObj}" > /log/w.log &

查看異步保存的日誌

tt 定位異常調用

上面所介紹的 watch可以排查函數的調用情況,比較適用在已知當次調用可能存在的情況後,查看信息。如果一個函數調用n次後,有幾次為執行異常,我們要去找出這些異常的調用,在 watch中排查就不怎麼方便了。使用 tt命令可以較方便查看異常的調用及信息。對 com.ytao.service.UserServiceImpl#getUser的函數查看, -t是每次調用該函數都會記錄

tt -t com.ytao.service.UserServiceImpl getUser

記錄信息

查看所有記錄

tt -l

查看指定函數記錄

tt -s 'method.name=="getUser"'

輸出信息說明

從上面參數中我們看到 1003調用是以拋異常的形式結束,因為 tt會記錄每次調用的信息,所以我們可以查看 1003的詳細信息

tt -i 1003

trace 查看調用鏈路

我們常會遇到調用某個api時rt過長,我們就要找出調用鏈上的某個或幾個函數進行優化,我們通常定位幾個可能的錨點,列印各個錨點間的rt。或者從日誌中找出日誌列印的時間點計算出時間差,不管使用哪種方法都比較繁瑣。當使用 arthas的 trace命令可以輕鬆的完成我們的需求。trace參數說明

使用 trace輸出 com.ytao.controller.UserController#getUser的信息

trace com.ytao.service.UserServiceImpl getUser

輸出結果

在實際使用使用排查過程中,為了減少無用信息的輸出,我們一般會使用 #cost過濾耗時不長和jdk自帶的函數,可以忽略的調用,減少信息的輸出。例如:過濾掉小於 1ms的調用

trace com.ytao.service.UserServiceImpl getUser '#cost > 1'

redefine 實現熱部署

當我們查找出bug,想要快速上線拯救蒼生的時候, Arthas為我們準備了 redefine命令來實現熱更新。儘管現在都在倡導 jad/ mc/ redefine熱更一條龍,但是線上代碼建議本地編譯好後再進行替換,避免手誤操作。首先先在 UserServiceImpl中添加一行代碼

獲取 classLoaderHash,通過 sc命令獲取類的信息

sc -d *UserServiceImpl

執行 redefine修改的類

redefine -c 1d56ce6a /usr/local/jar/UserServiceImpl.class

通過列印的信息驗證是否更新 UserServiceImpl類

Arthas的使用,除了上文中所講解到的,還有一些其他的診斷功能,這只是我個人使用的方法。但是使用該類工具一定要有套組合拳,對排查問題過程中,遇到問題有對應的排查手段,並非盲目排查。

文章來源: https://twgreatdaily.com/zh-cn/BmDcP24BMH2_cNUg9D2x.html