戰(zhàn):Java線上問題排查利器,無需重啟即可診斷JVM)
真香定律放在技術(shù)圈一樣適用。以前總覺得線上排查問題靠日志、靠監(jiān)控、靠人肉加日志就夠用了直到真正把 Arthas 用起來才發(fā)現(xiàn)以前那些操作有多繞。Arthas 是一個(gè)開源的 Java 診斷工具不用改代碼、不用重啟服務(wù)可以直接 attach 到正在運(yùn)行的 JVM 進(jìn)程上實(shí)時(shí)查看類加載情況、方法出入?yún)?、調(diào)用棧、線程狀態(tài)、JVM 指標(biāo)還能反編譯和在線熱更新。它專門解決一類問題線上偶發(fā)、日志不全、無法本地復(fù)現(xiàn)、一重啟現(xiàn)場就沒了。無論你是后端開發(fā)、維護(hù)過老項(xiàng)目還是正在被“線上 bug 怎么都復(fù)現(xiàn)不了”折磨這篇文章都值得花十分鐘讀完。1. 為什么說它是排查線上問題的“真香”工具1.1 傳統(tǒng)排查方式的三座大山先聊聊大家最熟悉的排查流程。遇到線上故障第一反應(yīng)通常是翻日志。但日志工具抄出來的往往是“結(jié)果”不是“過程”很多關(guān)鍵參數(shù)、臨時(shí)變量、調(diào)用時(shí)序根本不存在。如果日志里什么都有那問題可能早就被監(jiān)控平臺發(fā)現(xiàn)了現(xiàn)實(shí)往往是日志不夠、不夠細(xì)、沒有參數(shù)上下文。接著你會想本地復(fù)現(xiàn)把線上數(shù)據(jù)導(dǎo)出來構(gòu)造請求打一遍結(jié)果發(fā)現(xiàn)本地怎么都復(fù)現(xiàn)不出來。因?yàn)椴l(fā)量、緩存狀態(tài)、時(shí)鐘順序、臟數(shù)據(jù)都可能影響最終結(jié)果本地環(huán)境永遠(yuǎn)只是線上環(huán)境的一種近似。最后一招是加日志重啟改一行 log.info編譯打包發(fā)版等流量進(jìn)入再看日志這中間耗時(shí)少說半小時(shí)多則幾個(gè)小時(shí)而且頻繁重啟會讓現(xiàn)場被破壞流量重放也未必能還原問題。這三座大山我相信每個(gè)后端都經(jīng)歷過。Arthas 恰好能繞開它們不需要提前埋點(diǎn)因?yàn)樗茉谶\(yùn)行時(shí)直接查看真實(shí)出入?yún)⒉恍枰磸?fù)發(fā)布因?yàn)樗苯硬僮鳟?dāng)前進(jìn)程不需要靠猜因?yàn)樗馨颜{(diào)用路徑和方法執(zhí)行細(xì)節(jié)攤開給你看。用過一次之后你會真心覺得“真香”。1.2 原理attach 與字節(jié)碼增強(qiáng)要理解 Arthas 為什么能做到這些得先知道它的原理基礎(chǔ)。Arthas 本質(zhì)上利用了 Java 的 attach 機(jī)制和字節(jié)碼增強(qiáng)技術(shù)。attach 是 JDK 提供的一種能力允許一個(gè) JVM 進(jìn)程運(yùn)行時(shí)連接另一個(gè) JVM 進(jìn)程并通過 Agent 方式執(zhí)行操作。字節(jié)碼增強(qiáng)則是在類加載或運(yùn)行時(shí)對已經(jīng)加載的類做動(dòng)態(tài)修改在方法執(zhí)行前后插入攔截邏輯相當(dāng)于給方法裝上一套“臨時(shí)探針”。這套探針是臨時(shí)的JVM 一重啟就沒了不會影響原有邏輯。原理本身在 Java 生態(tài)里不算多新鮮但真正難的是把這一套能力封裝得好用做成命令行交互式的產(chǎn)品。Arthas 做到的正是這一點(diǎn)從啟動(dòng)到使用幾乎不需要在應(yīng)用代碼里增加任何依賴這對排查場景來說是極大的優(yōu)勢。1.3 什么時(shí)候該想到用它很多人問什么場景下 Arthas 才是剛需。我的判斷標(biāo)準(zhǔn)很簡單只要你遇到“日志看不出來、重啟代價(jià)高、需要確認(rèn)運(yùn)行期狀態(tài)”這三個(gè)條件中的兩個(gè)就該考慮它。典型場景包括接口耗時(shí)突然變長、CPU 飆升、線程池被打滿、緩存數(shù)據(jù)異常、灰度環(huán)境某個(gè)請求表現(xiàn)不一致、老項(xiàng)目沒有源碼無法重新編譯。這里有一個(gè)容易被忽略的點(diǎn)Arthas 不是只給“救火”用的。在正常的壓測、聯(lián)調(diào)、性能分析階段也可以主動(dòng)用它。我經(jīng)常在壓測時(shí)開著 dashboard一邊加壓一邊觀察線程池大小、GC 頻率和熱點(diǎn)方法比事后看測試報(bào)告直觀得多。因?yàn)樗皇菑慕y(tǒng)計(jì)數(shù)據(jù)里做推測而是直接從運(yùn)行中的 JVM 里拿事實(shí)。1.4 為什么團(tuán)隊(duì)?wèi)?yīng)該統(tǒng)一學(xué)習(xí)Arthas 這類工具個(gè)人會用和團(tuán)隊(duì)都會用的效果差別很大。一個(gè)人掌握遇到問題只能是他上團(tuán)隊(duì)都掌握任何值班的人都能快速定位初級問題不用每次都把核心開發(fā)叫起來看服務(wù)器。我見過不少團(tuán)隊(duì)把 Arthas 列為后端必修技能要求每個(gè)人至少掌握 dashboard、thread、watch、trace 四個(gè)命令的基本用法我覺得是非常務(wù)實(shí)的做法。2. 上手必須先掌握的 6 個(gè)高頻命令命令速查表先放在前面后面會逐個(gè)細(xì)講命令用途典型場景dashboard全局面板CPU、內(nèi)存、GC、線程狀態(tài)一眼看全watch觀察方法出入?yún)⒋_認(rèn)參數(shù)是否正常、異常在哪拋出trace方法調(diào)用鏈耗時(shí)定位慢請求卡在哪個(gè)子調(diào)用thread線程分析CPU飆高、死鎖、線程阻塞jad反編譯看線上實(shí)際運(yùn)行的代碼mc redefine熱更新緊急修復(fù)后臨時(shí)止損2.1 啟動(dòng)與選擇進(jìn)程java -jar arthas-boot.jarArthas 的使用路徑非常直接。第一步下載 arthas-boot.jar放到服務(wù)器某個(gè)目錄下執(zhí)行java -jar arthas-boot.jar啟動(dòng)后它會自動(dòng)列出當(dāng)前機(jī)器上所有 Java 進(jìn)程輸入序號就能 attach。如果想跳過交互直接選進(jìn)程可以在后面直接加 PIDjava -jar arthas-boot.jar 1234512345 是目標(biāo)進(jìn)程的進(jìn)程號。剛上手時(shí)我建議大家執(zhí)行 help 看看當(dāng)前版本支持的命令列表因?yàn)椴煌姹局g命令細(xì)節(jié)會有差異老是照著老資料敲容易踩坑。有一個(gè)小細(xì)節(jié)Arthas 本身運(yùn)行需要 JDK但需要 attach 的目標(biāo)進(jìn)程不一定要用 JDK 啟動(dòng)部分 JRE 環(huán)境也可以兼容。不過實(shí)際部署中建議用和運(yùn)行環(huán)境同一版本的 JDK 去啟動(dòng) Arthas 客戶端減少無謂的兼容性問題。attach 成功后會進(jìn)入交互終端提示符變成$所有命令都在這里輸入。2.2 dashboard 一眼看全局attach 成功后的第一個(gè)建議命令是 dashboard。它會實(shí)時(shí)刷新一個(gè)面板展示 CPU 占用率、內(nèi)存使用、GC 次數(shù)、線程狀態(tài)分布、每個(gè)線程的 CPU 消耗等信息。這等于給 JVM 裝了一個(gè)實(shí)時(shí)監(jiān)控屏幕打開它就能快速判斷問題在哪一層是 CPU 整體打滿還是內(nèi)存上漲觸發(fā)頻繁 GC還是某個(gè)線程卡死。初次使用的人容易被大量指標(biāo)淹沒我一般只看四樣?xùn)|西最上面的 CPU 使用率、內(nèi)存區(qū)域變化、GC 頻率、線程狀態(tài)里 BLOCKED 和 WAITING 的數(shù)量。如果 GC 頻率異常優(yōu)先排查堆內(nèi)存和對象分配如果有大量 BLOCKED 線程優(yōu)先找鎖競爭。面板是持續(xù)刷新的按 q 可以退出回到交互終端。2.3 watch 查看入?yún)?、返回值和異常watch 是線上排查使用頻率最高的命令沒有之一。它的作用是對指定方法觀察當(dāng)方法被調(diào)用時(shí)打印出入?yún)ⅰ⒎祷刂祷蛘弋惓P畔?。watch com.example.order.OrderServiceImpl createOrder {params, returnObj, throwExp} -x 2這條命令的意思是觀察 OrderServiceImpl 類的 createOrder 方法當(dāng)它執(zhí)行時(shí)打印入?yún)ⅰ⒎祷刂岛彤惓ο笄短渍归_深度 2 層。這里用了 OGNL 表達(dá)式用單引號包裹內(nèi)置變量包括 params 數(shù)組、returnObj 和 throwExp。我第一次用 watch 排查問題時(shí)整個(gè)人的感覺就是開了天眼。以前要加日志重新發(fā)版才能看到的參數(shù)現(xiàn)在直接在當(dāng)前進(jìn)程里實(shí)時(shí)看到而且可以臨時(shí)加條件只觀察特定用戶或特定業(yè)務(wù)類型watch com.example.order.OrderServiceImpl createOrder {params[0]} params[0].userId 10086 -x 3條件表達(dá)式寫最后返回布爾值只有滿足條件才打印。這樣在流量大的接口下看不會被無關(guān)請求刷屏。2.4 trace 定位方法耗時(shí)分布如果說 watch 解決的是“參數(shù)對不對”trace 解決的就是“時(shí)間花在哪”。它會輸出指定方法的調(diào)用鏈路上每個(gè)子方法的耗時(shí)自動(dòng)計(jì)算總耗時(shí)和每個(gè)節(jié)點(diǎn)耗時(shí)執(zhí)行效果類似一棵調(diào)用樹trace com.example.order.OrderServiceImpl createOrder命令執(zhí)行后目標(biāo)方法一旦被調(diào)用你會看到類似下面的輸出---createOrder() # 總耗時(shí) 823ms ---checkStock() # 56ms ---deductBalance() # 720ms ---sendMessage() # 47ms順著耗時(shí)最大的節(jié)點(diǎn)繼續(xù)往下 trace逐層鉆取就能鎖定真正慢的底層方法。這個(gè)過程就像剝洋蔥把慢請求一層層拆到最小執(zhí)行單元。有一點(diǎn)要提醒trace 是通過字節(jié)碼增強(qiáng)實(shí)現(xiàn)的它只增強(qiáng)執(zhí)行命令之后發(fā)生的調(diào)用歷史調(diào)用不會回溯。所以想抓到想要的 trace 結(jié)果必須讓目標(biāo)請求再進(jìn)來一次。如果接口觸發(fā)頻率低可以配合定時(shí)任務(wù)或者手動(dòng)打一個(gè)測試請求。2.5 thread比 jstack 更實(shí)用的線程分析很多同學(xué)排查線程問題只會 jstack但 jstack 一下輸出大量線程棧分不清哪個(gè)線程才是問題源頭。Arthas 的 thread 命令可以直接按 CPU 消耗排序這個(gè)能力非常實(shí)用thread -n 3它會打印出 CPU 占用最高的 3 個(gè)線程同時(shí)帶上對應(yīng)線程棧。對比 jstack 還得自己去數(shù)線程 ID、對應(yīng) 16 進(jìn)制換算效率高了一個(gè)量級。它還能直接查死鎖thread -bFound one Java-level deadlock: thread-1: waiting to lock monitor ...thread -b會直接輸出檢測結(jié)果和相關(guān)線程棧不用再從 jstack 輸出里手工解析 Locked ownable synchronizers。如果需要看指定線程可以帶線程 ID 精確查看這個(gè)在分析阻塞場景時(shí)很管用。2.6 jad mc redefine 實(shí)現(xiàn)在線熱更新這一組命令要謹(jǐn)慎使用但在緊急情況下非常救命。jad 用于反編譯已加載的類可以看線上運(yùn)行的真實(shí)代碼而不是本地倉庫里可能已經(jīng)過期的版本mc 是內(nèi)存編譯基于 jad 查看后的源碼在內(nèi)存里重新編譯redefine 是把編譯好的字節(jié)碼替換到運(yùn)行中的 JVM 里。jad --source-only com.example.order.OrderServiceImpl /tmp/OrderServiceImpl.java mc /tmp/OrderServiceImpl.java -d /tmp redefine /tmp/com/example/order/OrderServiceImpl.class熱更新最典型的使用場景是線上有 bug 需要臨時(shí)改一行邏輯發(fā)布流程要走審批這時(shí)先用 Arthas 做臨時(shí)修復(fù)讓服務(wù)先恢復(fù)然后馬上走正規(guī)發(fā)布。要注意這個(gè)操作不會改磁盤上的 class 文件也不會寫回 jar 包服務(wù)一重啟就失效。而且不是所有代碼都能熱更新成功增加類、改變方法簽名的場景通常會失敗。3. 實(shí)戰(zhàn)從幾個(gè)真實(shí)故障看 Arthas 的實(shí)際用法3.1 場景一CPU 飆高快速定位到正則災(zāi)難性回溯我先講一個(gè)最常見的 CPU 100% 問題。某服務(wù)在活動(dòng)流量上來后CPU 使用率持續(xù)接近滿核業(yè)務(wù)方反饋接口響應(yīng)很慢。按老辦法先看 top 找到進(jìn)程 PID然后 jstack 導(dǎo)出線程棧再做 16 進(jìn)制轉(zhuǎn)換找到對應(yīng)線程整個(gè)過程相當(dāng)繁瑣。用 Arthas 的流程會快很多。attach 之后先執(zhí)行 dashboard整體看一眼 CPU 和線程狀態(tài)當(dāng)時(shí)很明顯看到幾個(gè)線程占用 CPU 非??鋸垺H缓髨?zhí)行thread -n 5它直接給出了 CPU 最高的 5 個(gè)線程及其調(diào)用棧。我看到一個(gè)線程棧反復(fù)停在同一個(gè)業(yè)務(wù)方法里跟進(jìn) trace 這個(gè)方法發(fā)現(xiàn)內(nèi)部有一段非常復(fù)雜的字符串正則匹配在特定輸入下觸發(fā)災(zāi)難性回溯CPU 全部耗在了正則引擎內(nèi)部。定位過程前后不到五分鐘全程沒有重啟服務(wù)。定位到原因之后我用 jad 反編譯出當(dāng)前代碼把正則邏輯臨時(shí)替換成樸素的字符串前綴匹配通過 mc 和 redefine 熱更新到線上進(jìn)程服務(wù)立刻恢復(fù)。這個(gè)案例給我的最大感觸是CPU 飆高和接口慢本質(zhì)上都是“定位問題”的效率問題。只要能看到線程在干什么排查時(shí)間就能從小時(shí)級縮短到分鐘級。3.2 場景二接口偶發(fā)超時(shí)trace 逐層揪出隱藏阻塞另一個(gè)案例是某接口偶發(fā)超時(shí)頻率不高大概每天幾次。這種偶發(fā)問題最折磨人日志里偶爾看到超時(shí)但不知道卡在哪個(gè)環(huán)節(jié)。我先用 watch 看接口入?yún)l(fā)現(xiàn)超時(shí)請求和正常請求的參數(shù)沒有明顯區(qū)別于是改用 trace 觀察調(diào)用鏈路。第一次 trace 還沒有抓到超時(shí)等了差不多十幾分鐘才有一次超時(shí)出現(xiàn)。這次 trace 輸出里接口方法總耗時(shí)接近 3 秒其中一個(gè)下游 RPC 調(diào)用的子節(jié)點(diǎn)耗時(shí)占 2.5 秒其他子節(jié)點(diǎn)都只有幾十毫秒。順著這個(gè)異步下游繼續(xù)往下定位最后發(fā)現(xiàn)是某個(gè)配置中心客戶端的刷新邏輯在特定時(shí)刻阻塞導(dǎo)致請求串行等待。這個(gè)例子想說明一個(gè)關(guān)鍵點(diǎn)很多偶發(fā)問題根本不體現(xiàn)在業(yè)務(wù)日志里因?yàn)闃I(yè)務(wù)代碼里沒有打印每一次下游調(diào)用的耗時(shí)明細(xì)。Arthas 的運(yùn)行時(shí)觀察能力等于給線上系統(tǒng)裝了一臺顯微鏡它能把正常日志看不到的細(xì)節(jié)全部拉到眼前。3.3 場景三驗(yàn)證修復(fù)方案不用發(fā)布就能試還有一種很常見的用法不是緊急救火而是驗(yàn)證方案。曾經(jīng)有一個(gè)灰度問題我們懷疑是某個(gè)枚舉映射導(dǎo)致數(shù)據(jù)錯(cuò)亂但不敢完全確定。改代碼發(fā)布會話成本高而且如果改完還是錯(cuò)的等于白折騰。我先用 jad 反編譯出線上實(shí)際運(yùn)行的類確認(rèn)枚舉字段順序和本地代碼不一致這是一個(gè)非常關(guān)鍵的信息。因?yàn)楸镜卮a經(jīng)過多次迭代線上的 class 可能已經(jīng)和 git 分支對不上。確認(rèn)問題后我在本地改好用 mc 和 redefine 熱更新到灰度進(jìn)程再跑一次請求問題復(fù)現(xiàn)時(shí)已經(jīng)不出現(xiàn)了這才放心走正式發(fā)布流程。很多人擔(dān)心熱更新有副作用。我的經(jīng)驗(yàn)是先在測試環(huán)境或灰度環(huán)境做驗(yàn)證確認(rèn)無誤后再對生產(chǎn)操作操作完成之后立刻用 watch 或重新發(fā)一次請求確認(rèn)行為符合預(yù)期。熱更新在 JVM 層是支持的但生產(chǎn)環(huán)境的變更仍要符合團(tuán)隊(duì)變更流程最好有同事在場一起確認(rèn)至少留一條操作記錄。4. 常見問題與排查技巧實(shí)錄4.1 attach 不上目標(biāo)進(jìn)程怎么辦我遇到最多的問題是 attach 失敗報(bào)錯(cuò)或者列不出進(jìn)程。這里有一個(gè)速查表基本覆蓋我遇到過的原因常見原因現(xiàn)象處理建議用戶權(quán)限不一致attach 時(shí)報(bào)錯(cuò)或無權(quán)限切換成目標(biāo)進(jìn)程同一用戶啟動(dòng)/tmp 空間滿了attach 后立刻異常退出清理 /tmp 下臨時(shí)文件目標(biāo)進(jìn)程已退出進(jìn)程列表里找不到用 jps 確認(rèn)進(jìn)程狀態(tài)端口被占用telnet 連接失敗指定新端口啟動(dòng)Linux 下 attach 有權(quán)限限制最好用和目標(biāo)進(jìn)程相同的用戶操作。Arthas attach 時(shí)會在 /tmp 寫臨時(shí)文件空間不足會導(dǎo)致各種奇怪問題。如果還是不行可以在啟動(dòng) arthas-boot.jar 時(shí)帶上--telnet-port和--http-port參數(shù)換兩個(gè)沒被占用的端口很多端口沖突的問題就解決了。4.2 對線上服務(wù)的影響到底有多大這是每個(gè)初次接觸的人都會問的問題。字節(jié)碼增強(qiáng)確實(shí)會帶來一些性能開銷但實(shí)際影響通常很小。我的實(shí)測經(jīng)驗(yàn)是在常規(guī)業(yè)務(wù)方法上開啟 watch 或 trace單次調(diào)用的額外開銷在幾毫秒以內(nèi)對整個(gè)請求來說幾乎感知不到。但如果是對超級熱點(diǎn)方法開 trace比如某個(gè)每秒調(diào)用上萬次的基礎(chǔ)工具方法開銷就會放大。這種基礎(chǔ)方法被所有上層業(yè)務(wù)調(diào)用攔截和記錄的成本會被放大到不可忽略。我的建議是精準(zhǔn)選擇目標(biāo)方法不要對整個(gè)包路徑做通配觀察觀察期間留意 dashboard 的 CPU 變化如果發(fā)現(xiàn)明顯升高立刻縮小范圍或者停掉觀察命令。4.3 權(quán)限控制不能忽視Arthas 很強(qiáng)強(qiáng)到可以隨時(shí)查看進(jìn)程內(nèi)數(shù)據(jù)、修改運(yùn)行中的代碼所以它也是一把雙刃劍。在團(tuán)隊(duì)里使用至少要做到三件事控制在指定的運(yùn)維和開發(fā)人員范圍內(nèi)使用不要全員開放服務(wù)器權(quán)限生產(chǎn)環(huán)境使用前提前申請臨時(shí)權(quán)限并記錄操作原因操作完成后及時(shí)退出交互終端不要長期掛著會話。有些團(tuán)隊(duì)會把 Arthas 封裝成內(nèi)部平臺通過 Web 界面暴露受限接口避免開發(fā)人員直接登錄服務(wù)器亂敲命令。我比較認(rèn)可這種思路能力強(qiáng)大和可控并不矛盾。安全這塊做到位工具才能長期用下去。4.4 部分命令在生產(chǎn)環(huán)境不可用跟具體 JDK 版本相關(guān)的問題也值得關(guān)注。某些 JDK 版本下 jad 反編譯可能不完美尤其是涉及 Lambda 表達(dá)式或者動(dòng)態(tài)代理生成的類時(shí)反編譯結(jié)果會讓人看得很困惑。redefine 的限制更多對新增成員變量、改變方法簽名這類結(jié)構(gòu)性變更通常不支持強(qiáng)行操作會報(bào)錯(cuò)。遇到這種情況不要死磕熱更新。先用 watch 拿到足夠信息確認(rèn)根因后該走發(fā)布流程就走發(fā)布流程。工具的作用是幫你看清現(xiàn)場而不是替代正常的變更管理。5. 使用經(jīng)驗(yàn)和技巧分享5.1 條件表達(dá)式讓 watch 不再刷屏watch 默認(rèn)打印所有請求在流量大的接口下輸出會刷得飛快關(guān)鍵是里面有價(jià)值的請求可能只占很小比例。用條件表達(dá)式可以做到精準(zhǔn)觀察比如只看某類訂單創(chuàng)建失敗的請求watch com.example.order.OrderServiceImpl createOrder {params[0], returnObj} params[0].orderType 2 returnObj.success false條件表達(dá)式寫在命令最后返回布爾值滿足條件才觸發(fā)打印。這個(gè)技巧能讓你在繁忙環(huán)境下直接鎖定目標(biāo)請求不用從大量日志輸出里去撈。5.2 ognl 命令主動(dòng)觸發(fā)靜態(tài)方法調(diào)用有些時(shí)候你不僅想看還想主動(dòng)觸發(fā)某個(gè)方法比如清理某個(gè)靜態(tài)緩存、查看某個(gè)配置項(xiàng)。Arthas 提供 ognl 命令可以不寫代碼直接執(zhí)行靜態(tài)方法調(diào)用ognl com.example.config.CacheManagerclear()這種操作在應(yīng)急時(shí)很有效但風(fēng)險(xiǎn)也高它相當(dāng)于直接往 JVM 里執(zhí)行代碼。我建的建議是先把命令在測試環(huán)境驗(yàn)證一次再上生產(chǎn)并且必須明確這個(gè)調(diào)用會影響什么、會不會有并發(fā)問題。5.3 腳本批量采集線程現(xiàn)場遇到周期性出現(xiàn)的問題比如每小時(shí)整點(diǎn)偶發(fā)一次卡頓單次執(zhí)行 thread 不一定能抓到現(xiàn)場??梢杂靡粭l簡單的 shell 循環(huán)把 Arthas 的批處理模式用起來while true; do echo --- $(date) --- thread_dump.log java -jar arthas-boot.jar PID -c thread -n 5 -i 1000 thread_dump.log 21 sleep 5 done這里用到了-c參數(shù)可以讓 Arthas 執(zhí)行一條命令后自動(dòng)退出非常適合在腳本里批量使用。-i 1000是采樣間隔單位是毫秒。把多次采集到的線程快照放在一起對比周期性問題的觸發(fā)特征就很容易暴露出來。5.4 工具是幫手不是萬能藥最后說一點(diǎn)個(gè)人觀察。Arthas 確實(shí)香但解決的是“運(yùn)行態(tài)信息不可見”的問題很多線上故障的根因其實(shí)是代碼邏輯、容量規(guī)劃或者架構(gòu)設(shè)計(jì)。工具能幫你更快定位問題但定位完之后該重構(gòu)要重構(gòu)、該擴(kuò)容要擴(kuò)容不能把熱更新當(dāng)成常態(tài)化修復(fù)方式。我現(xiàn)在的習(xí)慣是復(fù)雜問題先用 Arthas 快速觀察現(xiàn)場拿到關(guān)鍵證據(jù)后把結(jié)論和操作記錄整理到運(yùn)維工單然后走穩(wěn)定的修復(fù)流程。工具負(fù)責(zé)把定位時(shí)間從小時(shí)降到分鐘剩下的工程質(zhì)量問題還是要靠扎實(shí)的代碼和可靠的變更流程解決。前后踩過幾次坑之后我對 Arthas 的態(tài)度已經(jīng)從“要不要學(xué)”變成了“怎么用得更穩(wěn)”。如果你也被線上問題復(fù)現(xiàn)難、定位慢困擾別猶豫找個(gè)低峰期在測試環(huán)境把 watch、trace、thread 這幾個(gè)基礎(chǔ)命令練熟真到救火那一刻你會感謝這個(gè)“真香”工具。