
前言
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命令查看,這里給出了一些例子和參數說明
[root@izwz94a0v1sz0gk4rezdcbz arthas]# java -jar arthas-boot.jar -h
[INFO] arthas-boot version: 3.1.4
Usage: arthas-boot [-h] [--target-ip <value>] [--telnet-port <value>]
[--http-port <value>] [--session-timeout <value>] [--arthas-home <value>]
[--use-version <value>] [--repo-mirror <value>] [--versions] [--use-http]
[--attach-only] [-c <value>] [-f <value>] [--height <value>] [--width
<value>] [-v] [--tunnel-server <value>] [--agent-id <value>] [--stat-url
<value>] [pid]
Bootstrap Arthas
EXAMPLES:
java -jar arthas-boot.jar <pid>
java -jar arthas-boot.jar --target-ip 0.0.0.0
java -jar arthas-boot.jar --telnet-port 9999 --http-port -1
java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'
java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'
--agent-id bvDOe8XbTM2pQWjF4cfw
java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'
java -jar arthas-boot.jar -c 'sysprop; thread' <pid>
java -jar arthas-boot.jar -f batch.as <pid>
java -jar arthas-boot.jar --use-version 3.1.4
java -jar arthas-boot.jar --versions
java -jar arthas-boot.jar --session-timeout 3600
java -jar arthas-boot.jar --attach-only
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
WIKI:
https://alibaba.github.io/arthas
Options and Arguments:
-h,--help Print usage
--target-ip <value> The target jvm listen ip, default 127.0.0.1
--telnet-port <value> The target jvm listen telnet port, default 3658
--http-port <value> The target jvm listen http port, default 8563
--session-timeout <value> The session timeout seconds, default 1800
(30min)
--arthas-home <value> The arthas home
--use-version <value> Use special version arthas
--repo-mirror <value> Use special maven repository mirror, value is
center/aliyun or http repo url.
--versions List local and remote arthas versions
--use-http Enforce use http to download, default use https
--attach-only Attach target process only, do not connect
-c,--command <value> Command to execute, multiple commands separated
by ;
-f,--batch-file <value> The batch file to execute
--height <value> arthas-client terminal height
--width <value> arthas-client terminal width
-v,--verbose Verbose, print debug info.
--tunnel-server <value> The tunnel server url
--agent-id <value> The agent id register to tunnel server
--stat-url <value> The report stat url
<pid> Target pid
啟動
啟動arthas之前,先啟動一個springboot的應用。該demo在地址https://github.com/yangtao...
java -jar ytao-springboot-demo.jar
啟動arthas-boot.jar命令
java -jar arthas-boot.jar
這里注意需要啟動demo和arthas使用同一權限用戶,否則使用attach機制獲取不到進程信息(這里剛使用時沒注意,遇到過這個問題)。
例:root用戶啟動 demo,u1用戶啟動arthas時,打印信息 Can not find java process. Try to pass <pid> in command line.

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

啟動類Bootstrap#main的代碼

進程工具類ProcessUtils#select的代碼

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

選擇java進程,這里我們的ytao-springboot-demo是 1,選擇后會有連接信息
[INFO] arthas home: /root/.arthas/lib/3.1.4/arthas
[INFO] Try to attach process 22005
[INFO] Attach process 22005 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://alibaba.github.io/arthas
tutorials https://alibaba.github.io/arthas/arthas-tutorials
version 3.1.4
pid 17339
time 2019-10-17 02:29:06
dashboard 數據面板
使用dashboard命令,可以查看線程,內存,GC,以及Runtime信息

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

watch 函數執行信息
使用watch命令可以查看函數的執行信息。watch的參數列表(來自官網)
| 參數 | 參數說明 |
|---|---|
| class-pattern | 類名表達式匹配 |
| method-pattern | 方法名表達式匹配 |
| express | 觀察表達式 |
| condition-express | 條件表達式 |
| [b] | 在方法調用之前觀察 |
| [e] | 在方法異常之后觀察 |
| [s] | 在方法返回之后觀察 |
| [f] | 在方法結束之后(正常返回和異常返回)觀察 |
| [E] | 開啟正則表達式匹配,默認為通配符匹配 |
| [x:] | 指定輸出結果的屬性遍歷深度,默認為 1 |
當我們遇到線上數據bug時,我們一般處理的手段就是開發環境模擬線上數據,從生產日志中查找線索,再或者遠程debug。以上不管哪種排查手段,相對都是比較麻煩。
這時Arthas的watch可以幫助我們查看實時的代碼執行情況。使用觀察表達式可以查看函數的參數,返回值,異常信息。觀察表達式主要由OGNL表達式組成,所以可以編寫OGNL表達式來執行。
觀察表達式的變量
| 變量 | 變量說明 |
|---|---|
| params | 函數的入參 |
| returnObj | 函數的返回值 |
| throwExp | 異常信息 |
| target | 當前對象 |
查看一個函數的入參和返回值
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"'
輸出信息說明
| 表格字段 | 字段解釋 |
|---|---|
| INDEX | 時間片段記錄編號,每一個編號代表着一次調用,后續tt還有很多命令都是基於此編號指定記錄操作,非常重要。 |
| TIMESTAMP | 方法執行的本機時間,記錄了這個時間片段所發生的本機時間 |
| COST(ms) | 方法執行的耗時 |
| IS-RET | 方法是否以正常返回的形式結束 |
| IS-EXP | 方法是否以拋異常的形式結束 |
| OBJECT | 執行對象的hashCode(),注意,曾經有人誤認為是對象在JVM中的內存地址,但很遺憾他不是。但他能幫助你簡單的標記當前執行方法的類實體 |
| CLASS | 執行的類名 |
| METHOD | 執行的方法名 |
從上面參數中我們看到1003調用是以拋異常的形式結束,因為tt會記錄每次調用的信息,所以我們可以查看1003的詳細信息
tt -i 1003

trace 查看調用鏈路
我們常會遇到調用某個api時rt過長,我們就要找出調用鏈上的某個或幾個函數進行優化,我們通常定位幾個可能的錨點,打印各個錨點間的rt。或者從日志中找出日志打印的時間點計算出時間差,不管使用哪種方法都比較繁瑣。當使用arthas的trace命令可以輕松的完成我們的需求。
trace參數說明
| 參數 | 參數說明 |
|---|---|
| class-pattern | 類名表達式匹配 |
| method-pattern | 方法名表達式匹配 |
| condition-express | 條件表達式 |
| [E] | 開啟正則表達式匹配,默認為通配符匹配 |
| [n:] | 命令執行次數 |
| #cost | 方法執行耗時 |
使用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://ytao.top
我的公眾號 ytao

