Java在線診斷工具Arthas介紹

Arthas

Arthas是什么

Arthas 是Alibaba開源的Java診斷工具,深受開發(fā)者喜愛。
當你遇到以下類似問題而束手無策時,Arthas可以幫助你解決:
這個類從哪個 jar 包加載的?為什么會報各種類相關(guān)的 Exception?
遇到問題無法在線上 debug,難道只能通過加日志再重新發(fā)布嗎?
線上遇到某個用戶的數(shù)據(jù)處理有問題,但線上同樣無法 debug,線下無法重現(xiàn)!
——Arthas中文說明文檔

前幾天遇到了一個這樣的場景:

  1. 核心業(yè)務(wù)上線了新功能,在上線時間節(jié)點前后核心方法平均耗時同比增長了約15%
  2. 新功能包含一個第三方RPC調(diào)用,有可能出現(xiàn)服務(wù)不穩(wěn)定的風(fēng)險
  3. 新功能未增加耗時日志記錄,第三方RPC調(diào)用也未接入統(tǒng)計上報,導(dǎo)致在服務(wù)器日志及RPC鏈路監(jiān)控中都沒辦法了解耗時信息
  4. 新功能成了一個監(jiān)控盲點,上線前后耗時又有變化,于是這個功能成了首要懷疑對象

這是一個典型的線上Debug場景:加打點日志,重新發(fā)布。
Arthas作為一個診斷工具,提供了很多更高效的方法。

Arthas能做什么

Arthas通過JDK提供的Instrument(基于Java Virtual Machine Tool Interface)和asm庫(操作字節(jié)碼),在低侵入的情況下提供了非常豐富的在線診斷功能,包括JVM信息查詢、字節(jié)碼增強熱替換等。
幾個比較關(guān)鍵的功能是:

  • 查看JVM的一些信息,包括線程信息、加載的類的信息、系統(tǒng)屬性和環(huán)境變量等
  • 反編譯已加載類的代碼(.class to .java)
  • 內(nèi)存編譯器(.java to .class)
  • 加載外部.class文件并替換原本已加載的類

通過這幾個關(guān)鍵功能,Arthas可以實現(xiàn)無需重啟(甚至不需要登錄對應(yīng)的服務(wù)器)在線上環(huán)境Debug、臨時緊急修復(fù)等功能。
Arthas功能的確很豐富,但是使用不當也會造成一定的影響。

基礎(chǔ)命令

help 查看命令幫助
pwd 當前工作目錄,同Linux命令,結(jié)合cat命令使用
cat 打印文件內(nèi)容,同Linux命令,可用于查看機器配置內(nèi)容
history 歷史命令,同Linux命令
dashboard 實時數(shù)據(jù),包括線程信息、堆棧信息、系統(tǒng)變量

比較重要的命令

sc 和 sm

  • sc {Class-Pattern} Search-Class,搜索JVM已經(jīng)加載的類,比如sc *Controller
  • sm {Class-Pattern} Search-Method,搜索類中的方法,比如sm *QuestionController

getstatic
可以用于查看類的靜態(tài)屬性,比如用于查看當前加載的配置信息

    $ getstatic *ConfigUtil editWhiteList
    @HashSet[
        @Long[174446],
        @Long[175009],
        @Long[174957],
        @Long[21959],
        @Long[10519],
        @Long[23813],
        @Long[10034],
    ]

jad、mc、redefine

  • jad [--source-only] {Class-Pattern} [{Method}] 將指定的類反編譯為高亮代碼,可結(jié)合sm命令僅反編譯指定方法
  • mc {File} Memory-Compile,使用內(nèi)存編譯器將.java文件編譯為.class文件
  • redefine 加載外部.class文件,替換JVM中的類

通過這三個方法的結(jié)合可以實現(xiàn)在線反編譯、修改、編譯、替換的熱修復(fù)操作,但是實際上這個做法十分危險,很可能導(dǎo)致線上代碼處于不可控狀態(tài)。
更合理的方式是在本地修復(fù)并編譯通過后將生成的.class文件在線上替換。

thread

  • thread 查看當前JVM所有線程信息,ID一欄是JVM級別的。
$ thread
Threads Total: 160, NEW: 0, RUNNABLE: 16, BLOCKED: 0, WAITING: 109, TIMED_WAITING: 35, TERMINATED: 0
ID                 NAME                                                       GROUP                                  PRIORITY           STATE              %CPU                TIME               INTERRUPTED         DAEMON
284                as-command-execute-daemon                                  system                                 10                 RUNNABLE           56                  0:0                false               true
34                 cluster-ClusterId{value='5d31369656d4890864c2d2ff', descri main                                   5                  TIMED_WAITING      36                  0:0                false               true
36                 DubboRegistryFailedRetryTimer-thread-1                     main                                   5                  TIMED_WAITING      2                   0:0                false               true
50                 DubboResponseTimeoutScanTimer                              main                                   5                  TIMED_WAITING      1                   0:2                false               true
54                 JinJingRocketMQSender-thread-1                             main                                   5                  TIMED_WAITING      1                   0:0                false               true

可以看出來在Arthas接入期間診斷用的線程CPU占用率是比較高的。

  • thread {id} 查看指定線程的狀態(tài)
$ thread 284
        "as-command-execute-daemon" Id=284 RUNNABLE
          at sun.management.ThreadImpl.dumpThreads0(Native Method)
          at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
          at com.taobao.arthas.core.command.monitor200.ThreadCommand.processThread(ThreadCommand.java:146)
          at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:77)
          at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
          at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
          at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
          at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
          at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:370)
          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
          at java.lang.Thread.run(Thread.java:745)

          Number of locked synchronizers = 1
          - java.util.concurrent.ThreadPoolExecutor$Worker@5c49c9aa
  • thread -n {n} 打印最忙的n個線程的信息
$ thread -n 3
        "as-command-execute-daemon" Id=289 cpuUsage=95% RUNNABLE
          at sun.management.ThreadImpl.dumpThreads0(Native Method)
          at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
          at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:133)
          at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:79)
          at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
          at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
          at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
          at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
          at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:370)
          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
          at java.lang.Thread.run(Thread.java:745)
          Number of locked synchronizers = 1
          - java.util.concurrent.ThreadPoolExecutor$Worker@4b3798a4
        "DubboResponseTimeoutScanTimer" Id=50 cpuUsage=2% TIMED_WAITING
          at java.lang.Thread.sleep(Native Method)
          at com.alibaba.dubbo.remoting.exchange.support.DefaultFuture$RemotingInvocationTimeoutScan.run(DefaultFuture.java:300)
          at java.lang.Thread.run(Thread.java:745)
        "Abandoned connection cleanup thread" Id=23 cpuUsage=1% TIMED_WAITING on java.lang.ref.ReferenceQueue$Lock@47459f1b
          at java.lang.Object.wait(Native Method)
          -  waiting on java.lang.ref.ReferenceQueue$Lock@47459f1b
          at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
          at com.mysql.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:40)
  • thread -b 找出阻塞其他線程的線程

* 非常重要的命令

monitor
使用了字節(jié)碼增強并替換了原本運行中的類,用于查看方法的執(zhí)行統(tǒng)計信息,包括調(diào)用次數(shù)、平均耗時、失敗率

$ monitor -c 5 demo.MathGame primeFactors
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 94 ms.
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:38  demo.MathGame  primeFactors  5      1        4     1.15        80.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:43  demo.MathGame  primeFactors  5      3        2     42.29       40.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:48  demo.MathGame  primeFactors  5      3        2     67.92       40.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:53  demo.MathGame  primeFactors  5      2        3     0.25        60.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:58  demo.MathGame  primeFactors  1      1        0     0.45        0.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:07:03  demo.MathGame  primeFactors  2      2        0     3182.72     0.00%

trace
使用了字節(jié)碼增強并替換了原本運行中的類,用于查看方法的執(zhí)行耗時,對當前方法調(diào)用的每個方法都進行了打點計時,并標記最耗時的調(diào)用,只追蹤一級方法

$ trace demo.MathGame run
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 42 ms.
`---ts=2018-12-04 00:44:17;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[10.611029ms] demo.MathGame:run()
        +---[0.05638ms] java.util.Random:nextInt()
        +---[10.036885ms] demo.MathGame:primeFactors()
        `---[0.170316ms] demo.MathGame:print()

reset
monitor、trace命令都使用了字節(jié)碼增強替換,替換后的類邏輯上沒有變化,但是插入的一些切面有些許額外性能開銷的,不應(yīng)當追蹤過長的時間,在診斷完成之后應(yīng)當使用reset方法恢復(fù)所有增強過的類。

shutdown
關(guān)閉當前的Arthas客戶端,同時關(guān)閉當前連接,在關(guān)閉時會調(diào)用reset。
30分鐘未進行操作會自動shutdown,超時時間可配置。這應(yīng)該是考慮到Arthas不應(yīng)當長時間啟用、連接,如果忘記退出需要自動reset。

quit
關(guān)閉當前的Arthas客戶端,不會關(guān)閉當前連接,不會調(diào)用reset。

Arthas怎么用

方式1:使用jar

操作流程

  1. 登錄目標機器
  2. java -jar arthas-boot.jar(或者使用/path/to/java -jar /path/to/arthas-boot.jar --arthas-home /path/to/arthas/lib/
  3. 選擇要連接的進程id,Arthas會進行attach和連接

attach和連接是兩個步驟,attach是在目標進程中創(chuàng)建一個Arthas的守護線程,并監(jiān)聽對應(yīng)的端口,默認為8563。

使用jar操作的一些坑

  • Arthas和服務(wù)最好使用對應(yīng)的JDK版本
  • Arthas啟動時會訪問Maven倉庫下載依賴(默認使用阿里云,可配置),考慮到機房隔離問題,最好在需要部署的機器上將arthas-boot.jar和其依賴提前下載一起復(fù)制過去

方式2:使用WebConsole

操作流程

  1. 進入Arthas控制臺(需自己實現(xiàn))


  2. 選擇目標服務(wù),使用Arthas提供的WebConsole遠程連接


使用 WebConsole 遠程連接時的一些坑

  1. 目標機器Arthas未attach時無法連接,且需要確認attach的具體PID
  2. 需要一臺Arthas客戶端用于提供WebConsole服務(wù)
    • 其實也可以直接訪問目標機器的8563端口,但考慮到機房的隔離問題,最好使用一個跳板機,通過跳板機連接目標機器
  3. 連接到同一臺機器的客戶端會共用一個連接,因此連接其他服務(wù)前應(yīng)當斷開當前連接
  4. quit命令不會斷開連接,需要shutdown,而其中一個客戶端使用shutdown所有共用連接的客戶端都會退出

目前為了使用 WebConsole 做了哪些措施

  1. 修改了目標機器上原有的一個Java Agent,提供了一個Http接口,用于:
    • 喚起目標機器上的Arthas
    • 查找PID并attach到目標進程
  2. 開發(fā)Arthas控制臺,在控制臺點擊“啟用Arthas”后做了一系列操作:
    • 如果本機Arthas未啟動,喚起控制臺本機的Arthas
    • 調(diào)用目標機器Agent的喚起接口,attach到對應(yīng)的進程
  3. 從控制臺機器上的Arthas客戶端連接目標機器的Arthas監(jiān)聽的端口

后續(xù)可優(yōu)化的點

  1. 可以集成到其他系統(tǒng)中,比如發(fā)布平臺、監(jiān)控平臺
  2. 可以增加熱修復(fù)的功能(需非常謹慎)
  3. 目前agent提供Http接口用于喚醒Arthas,跨機房的喚醒和連接需要建立機房間的代理或在每個機房都部署控制臺
  4. 目前使用Http接口喚醒目標機器的Arthas,可以改為監(jiān)聽Zookeeper的方式喚醒

Arthas相關(guān)網(wǎng)站

Github:alibaba/arthas: Alibaba Java Diagnostic Tool Arthas/Alibaba Java診斷利器Arthas
中文文檔,有在線教程:arthas/README_CN.md at master · alibaba/arthas
命令介紹:進階使用 — Arthas 3.1.1 文檔

參考資料

記錄如何使用arthas進行遠程訪問 · Issue #442 · alibaba/arthas

本文搬自我的博客,歡迎參觀!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380