前言
有些線上問題,乍一看像玄學。
接口突然變慢了,日志沒報錯,機器負載也不高,開發(fā)同學圍著屏幕看了半天,最后得出一個非常有技術含量的結(jié)論:再觀察觀察。
如果你也經(jīng)歷過這種場面,那大概率會需要 Arthas。
它不是銀彈,但它非常像一個能隨時插進 JVM 里“看現(xiàn)場”的老師傅。程序已經(jīng)跑起來了,不用重啟,不用提前埋一堆日志,也不用一邊改代碼一邊祈禱“希望這次能復現(xiàn)”。很多時候,Arthas 的價值就一句話:
出問題的時候,它能讓你少靠腦補,多看事實。
這篇文章不打算把 Arthas 所有命令都鋪滿一屏,那樣讀完只會讓人想關網(wǎng)頁。我們就聊最常用、最接地氣的一部分,爭取做到:
- 看得懂
- 用得上
- 下次線上出事時,能想起來先開 Arthas,而不是先拜祖師爺
一、Arthas 到底是干嘛的?
簡單說,Arthas 是一個 Java 診斷工具,可以在應用運行時查看類、方法、線程、JVM 狀態(tài),還能動態(tài)跟蹤方法調(diào)用、觀察參數(shù)和返回值,甚至臨時執(zhí)行一些排查動作。
你可以把它理解成:
-
top、jstack、日志、臨時調(diào)試輸出的一個“加強縫合版” - 給線上 JVM 開了一個“觀察窗”
- 程序員深夜救火時,情緒穩(wěn)定器
它最適合的場景一般有這些:
- 某個接口為什么變慢了
- 某個方法到底有沒有被調(diào)用
- 傳進去的參數(shù)到底是什么鬼
- 返回值為什么和我想的不一樣
- CPU 飆高到底是哪幾個線程在鬧
- 某個類是不是我以為的那個版本
二、先啟動起來
最常見的啟動方式,一般是這樣:
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
運行后,它會列出當前機器上的 Java 進程,比如:
[1]: 12345 demo-application.jar
[2]: 23456 another-service.jar
輸入對應編號,Arthas 就會 attach 到目標進程。
這一步的感覺很像:
“你先別慌,讓我進去看看你腦子里現(xiàn)在到底在跑什么?!?/p>
如果你用的是 Spring Boot、Tomcat、微服務應用,流程都差不多。選中進程后,會進入 Arthas 命令行界面,接下來就能開始排查。
三、先記住這幾個高頻命令
Arthas 命令不少,但真正常用的,先掌握下面這些就夠你打很多場仗了。
1. dashboard:先看大盤,別一上來就瞎追
dashboard
它會展示當前 JVM 的一些整體信息,比如:
- 線程數(shù)量
- 內(nèi)存使用情況
- GC 情況
- 運行狀態(tài)
適合在剛連上系統(tǒng)時先掃一眼。
很多人排障像刑偵劇里的熱血新人,一上來就沖方法級別追蹤。其實更穩(wěn)妥的做法是先看全局:線程多不多?GC 抖不抖?是不是整體就已經(jīng)不正常了?
dashboard 就像醫(yī)生量血壓,雖然不能直接告訴你病根,但至少能先判斷病人是不是已經(jīng)快拍桌子了。
2. thread:誰在吃 CPU,一看便知
thread
如果你懷疑 CPU 高,可以直接看最忙的線程:
thread -n 5
這個命令會列出最占 CPU 的幾個線程,還能看到線程狀態(tài)。
如果某臺機器 CPU 飆得跟起飛一樣,這個命令特別實用。你可以先定位“是誰最能折騰”,再順著線程棧繼續(xù)查。
常見思路是:
- 先用
thread -n 5找出高 CPU 線程 - 看線程棧在跑什么代碼
- 再結(jié)合業(yè)務判斷是不是死循環(huán)、頻繁重試、鎖競爭或者某段邏輯特別重
很多線上 CPU 問題,說白了不是 JVM 發(fā)神經(jīng),而是代碼在努力把服務器送走。
3. sc:確認類到底在不在
sc -d com.example.demo.service.UserService
sc 是 search class,用來查類的信息。
常見用途:
- 這個類到底有沒有加載
- 是哪個 ClassLoader 加載的
- 類的源碼位置、類信息對不對
如果你懷疑“我明明改了代碼為什么沒生效”,那先別急著懷疑人生,先用 sc 看看加載的到底是不是你以為的那個類。
線上最怕的不是 bug,而是你以為線上跑的是 A,結(jié)果它跑的是 B,還特別自信。
4. jad:反編譯看看現(xiàn)場代碼
jad com.example.demo.service.UserService
這個命令非常實用。它可以把 JVM 里已經(jīng)加載的類反編譯出來。
有什么價值?
- 看線上運行的代碼到底長什么樣
- 排查部署包和本地代碼是不是不一致
- 驗證某個分支邏輯到底有沒有被打進去
很多時候,本地代碼、倉庫代碼、構建產(chǎn)物、線上運行代碼,這四位選手并不一定意見統(tǒng)一。
jad 的作用,就是讓你停止爭論,直接看證據(jù)。
5. sm:查某個類里有哪些方法
sm com.example.demo.service.UserService
如果你只記得類名,不記得具體方法簽名,可以先用 sm 看方法列表。
比如你想觀察一個方法,但名字記混了,或者重載太多,這時候先 sm 一下,比硬著頭皮亂寫要靠譜。
這命令像通訊錄:
“人我認識,但全名一下子想不起來,你先讓我翻一下?!?/p>
四、真正干活的幾個命令
上面的命令偏“看信息”,下面這些則更像“直接盯現(xiàn)場”。
也是 Arthas 最容易讓人上頭的部分。
1. watch:看方法參數(shù)、返回值、異常
這是最常用的命令之一。
比如:
watch com.example.demo.service.UserService getUser '{params, returnObj, throwExp}' -x 2
它的意思大概是:
- 觀察
UserService的getUser方法 - 輸出參數(shù)、返回值、異常信息
-
-x 2表示展開對象層級
這個命令很適合回答下面這些靈魂拷問:
- 這個方法到底收到了什么參數(shù)?
- 為什么查出來是
null? - 為什么它拋異常了?
- 調(diào)用鏈上游是不是傳了臟數(shù)據(jù)?
如果日志沒打夠,watch 就特別像臨時補了個高清監(jiān)控。
不過要注意,線上使用時別一把梭觀察太寬泛的方法,尤其是高頻調(diào)用的方法。否則你本來是去排障的,結(jié)果變成了手動制造更多輸出。
建議控制范圍:
- 盡量指定具體類和方法
- 必要時加條件表達式
- 先看少量樣本,別上來就“全量圍觀”
2. trace:慢在哪一層,給我攤開講
trace com.example.demo.controller.UserController queryUser
trace 可以追蹤一個方法內(nèi)部的調(diào)用耗時。
比如一個接口慢,你不確定慢在:
- 查數(shù)據(jù)庫
- 調(diào)遠程服務
- 數(shù)據(jù)轉(zhuǎn)換
- 某個 if 分支里的神秘邏輯
這時候 trace 很有用,它會把調(diào)用路徑和各層耗時列出來。
排查性能問題時,這命令基本屬于“別廢話,直接上證據(jù)”。
一個常見場景:
你以為慢是數(shù)據(jù)庫問題,結(jié)果一 trace,發(fā)現(xiàn) 80% 時間都花在 JSON 轉(zhuǎn)換上。
這時候你就會明白,很多性能問題不是“架構不行”,而是代碼在偷偷干體力活。
3. tt:記錄方法調(diào)用,像回放錄像
tt 全稱是 Time Tunnel,時間隧道。
名字很炫,功能也確實挺能打。
比如:
tt -t com.example.demo.service.OrderService createOrder
它會把這個方法的調(diào)用記錄下來。之后你可以查看這些調(diào)用記錄:
tt -l
還可以指定某條記錄查看詳情:
tt -i 1000
適合什么場景?
- 某個方法偶發(fā)異常,不容易穩(wěn)定復現(xiàn)
- 你想保留調(diào)用現(xiàn)場,后面慢慢看
- 想對同一個問題樣本反復分析
如果說 watch 像實時監(jiān)控,那 tt 就更像 DVR 回放。
問題發(fā)生的時候先錄下來,事后再一幀一幀看,氣質(zhì)都從慌張排障變成了從容辦案。
4. monitor:統(tǒng)計方法調(diào)用情況
monitor -c 5 com.example.demo.service.UserService getUser
這個命令會周期性統(tǒng)計某個方法的調(diào)用指標,比如:
- 調(diào)用次數(shù)
- 成功次數(shù)
- 失敗次數(shù)
- 平均響應時間
適合觀察一段時間內(nèi)的方法表現(xiàn),而不是只看單次調(diào)用。
比如接口報錯率升高,你想知道:
- 到底失敗多不多
- 是持續(xù)失敗還是偶發(fā)失敗
- 平均耗時是不是明顯上升了
這時候 monitor 很順手。
它像值班表,不負責講故事,但很適合告訴你:“這個人最近表現(xiàn)確實不太對勁?!?/p>
五、幾個很實戰(zhàn)的排查場景
光講命令容易看完就忘。下面直接上幾個常見場景。
場景一:接口很慢,但不知道慢在哪
思路可以這么走:
-
dashboard看整體 JVM 狀態(tài) -
thread -n 5看有沒有線程異常繁忙 -
trace跟蹤目標接口方法耗時 - 必要時對關鍵方法再用
watch看參數(shù)和返回值
適合的命令示例:
trace com.example.demo.controller.OrderController submit
watch com.example.demo.service.OrderService createOrder '{params, returnObj}' -x 2
這個流程的核心原則是:
先看全局,再盯局部;先定位范圍,再下鉆細節(jié)。
不要一上來就對整個系統(tǒng)瘋狂 watch,那不是排障,那是行為藝術。
場景二:接口返回結(jié)果不對
比如用戶說:“這個接口查出來的數(shù)據(jù)不對。”
先別急著甩鍋數(shù)據(jù)庫、緩存、第三方接口和水逆。
可以這樣查:
-
watch看 controller 或 service 方法入?yún)?/li> - 看返回值是不是從這里開始歪的
- 如果邏輯復雜,用
trace看分支耗時和調(diào)用過程 - 用
jad看線上實際代碼是不是你預期的版本
示例:
watch com.example.demo.service.UserProfileService queryProfile '{params, returnObj, throwExp}' -x 3
jad com.example.demo.service.UserProfileService
很多“結(jié)果不對”的問題,最后都不是框架鍋,而是某個參數(shù)一路傳著傳著,半路就長歪了。
場景三:偶發(fā)異常,日志又不完整
這個最煩。
因為它不穩(wěn)定、難復現(xiàn)、還喜歡在你下班前十分鐘出現(xiàn)。
這時候可以優(yōu)先考慮:
tt -t com.example.demo.service.PaymentService pay
把調(diào)用先錄下來,等異常出現(xiàn)后再:
tt -l
tt -i 1001
這樣至少能把出問題那次調(diào)用的參數(shù)、返回、異常現(xiàn)場抓住。
有時候排障最痛苦的不是問題難,而是你根本沒拿到現(xiàn)場。
tt 的意義,就是幫你把“案發(fā)錄像”留住。
六、Arthas 不是越猛越好,用的時候要有分寸
這部分很重要。
Arthas 很強,但不是讓你在線上隨便開“上帝視角”的理由。命令用得不克制,可能會帶來額外開銷,尤其是:
- 盯高頻方法
- 打印大對象
- 觀察范圍過寬
- 長時間持續(xù)輸出
幾個實用建議:
- 先縮小目標,不要一把抓全服務
- 優(yōu)先針對具體類、具體方法
- 對象展開層級別開太大,
-x 2、-x 3通常夠用了 - 用完及時停掉相關命令
- 在線上高峰期操作要更謹慎
說直白點:
Arthas 是手術刀,不是電鋸。
你是去定位問題的,不是去給線上服務做行為實驗的。
七、新手最容易踩的幾個坑
1. 方法名寫對了,結(jié)果就是沒輸出
可能原因:
- 類名沒寫全
- 方法簽名沒匹配上
- 目標方法根本沒被調(diào)用
- 你 attach 錯進程了
別笑,這個問題發(fā)生頻率相當高。很多時候不是 Arthas 不工作,是人工作得比較有想象力。
2. 輸出太多,把自己看暈了
這是 watch、trace 常見問題。
建議從最小范圍開始,先看一兩個請求,再逐步擴大,不要一開始就讓終端進入“瀑布模式”。
如果你看到輸出瘋狂滾動,心里開始發(fā)虛,通常說明你觀察得太猛了。
3. 只盯技術指標,不結(jié)合業(yè)務判斷
Arthas 能告訴你“發(fā)生了什么”,但不一定直接告訴你“為什么業(yè)務會這樣”。
比如你看到一個方法耗時高,不代表它一定有 bug;也可能是這個請求本來就需要查很多數(shù)據(jù)。
所以排查時一定要結(jié)合:
- 請求場景
- 業(yè)務入?yún)?/li>
- 調(diào)用鏈上下文
- 近期發(fā)布記錄
工具負責給證據(jù),人負責做判斷。
別把工具當算命先生。
八、給一份夠用的排障套路
如果你現(xiàn)在只想先記住一套“線上排障起手式”,可以記這個:
- 進入 Arthas,確認進程
- 用
dashboard看整體狀態(tài) - 用
thread看線程和 CPU - 用
sc、sm、jad確認類和方法沒找錯 - 用
trace找耗時點 - 用
watch看參數(shù)、返回值、異常 - 遇到偶發(fā)問題,用
tt留現(xiàn)場
這套不一定覆蓋所有復雜場景,但處理日常線上問題,已經(jīng)相當能打。
九、最后總結(jié)
Arthas 真正厲害的地方,不是命令多,也不是界面酷,而是它讓線上問題從“靠猜”變成“可觀察”。
以前很多排障過程像這樣:
- 我感覺是這里有問題
- 我猜可能是緩存
- 我懷疑是線程池
- 我覺得數(shù)據(jù)庫有點可疑
用了 Arthas 以后,風格會逐漸變成:
- 參數(shù)就是這樣傳進來的
- 這層調(diào)用耗時最高
- 這個線程確實在忙這個方法
- 線上跑的代碼就是這個版本
這就是區(qū)別。
它不會替你解決所有問題,但能讓你少走很多彎路。
對于 Java 開發(fā)來說,Arthas 非常值得熟悉,尤其是做線上排障、性能分析、問題定位的時候,幾乎屬于“會了不一定天天用,但真出事時會非常想它”。
如果要用一句大白話收尾,那就是:
線上出問題時,別急著拍腦袋,先讓 Arthas 說話。