天天看點

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

【Arthas 官方社群正在舉行征文活動,參加即有獎品拿哦~ 點選投稿

作者 | 楊桢棟,筆名叫蠻三刀把刀,是一名一線網際網路碼農,留美訪學一年,主要關注後端開發,資料安全,爬蟲,物聯網,邊緣計算等方向。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

前言

當你興沖沖地開始運作自己的 Java 項目時,你是否遇到過如下問題:

  • 程式在穩定運作了,可是實作的功能點了沒反應。
  • 為了修複 Bug 而上線的新版本,上線後發現 Bug 依然在,卻想不通哪裡有問題?
  • 想到可能出現問題的地方,卻發現那裡沒打日志,沒法在運作中看到問題,隻能加了日志輸出重新打包——部署——上線
  • 程式功能正常了,可是為啥響應時間這麼慢,在哪裡出現了問題?
  • 程式不但穩定運作,而且功能完美,但跑了幾天或者幾周過後,發現響應速度變慢了,是不是記憶體洩漏了?

以前,你碰到這些問題,解決的辦法大多是,修改代碼,重新上線。但是在大公司裡,上線的流程是非常繁瑣的,如果為了多加一行日志而重新釋出版本,無疑是非常折騰人的。

現在,我們有了更為優雅的線上調試方法 - 來自阿裡巴巴開源的 Arthas。

下圖是 Arthas 文檔中對于為什麼要使用它的描述,我進行了精簡:

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

好了,前言已經超過字數了,哈哈,在本篇文章裡,你能夠了解:

  • Arthas 使用執行個體:幫助你快速讓你上手,拯救你的低效率 Debug
  • 使用 Arthas 解決具體問題:看一下 Arthas 幫我拯救了多少時間
  • 相似工具:看看線上 Debug 還有沒有别的工具可以使用
  • 原理淺談:莫在浮沙築高閣!你需要大概了解下 Arthas 的原理

相信我,Arhas 絕對是你提升效率的利器,适合各種階段的開發者,尤其适合我這種剛入門的新人(天天上班寫 Bug 的人)。你不應該有這種東西是高階程式員才應該去使用的思想,放心大膽的去用吧!

線上 Debug 神器 Arthas

Arthas 使用執行個體

指令的詳細文檔請參考:

alibaba.github.io/arthas/comm…

快速啟動

快速啟動它,你隻需要兩行指令:

wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar           
Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

随後,在界面出現的程序中,選擇你的程式序号,比如 1

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

這樣你就進入了 arthas 的控制台。

基本使用

Arthas 有如下功能:

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

1. 首先是我認為的“上帝視角”指令:Dashboard

目前系統的實時資料面闆,按 ctrl+c 退出;

當運作在 Ali-tomcat 時,會顯示目前 tomcat 的實時資訊,如 HTTP 請求的 qps, rt, 錯誤數, 線程池資訊等等。

通過這些,你可以對于整個程式程序有個直覺的資料監控。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

2. 類加載問題相關指令

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

SC:檢視 JVM 已加載的類資訊

通過 SC 我們可以看到我們這個類的詳細資訊,包括是從哪個 jar 包讀取的,他是不是接口/枚舉類等,甚至包括他是從哪個類加載器加載的。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

上圖中代碼:

[arthas@37]$ sc -d *MathGame
 class-info        demo.MathGame
 code-source       /home/scrapbook/tutorial/arthas-demo.jar
 name              demo.MathGame
 isInterface       false
 isAnnotation      false
 isEnum            false
 isAnonymousClass  false
 isArray           false
 isLocalClass      false
 isMemberClass     false
 isPrimitive       false
 isSynthetic       false
 simple-name       MathGame
 modifier          public
 annotation
 interfaces
 super-class       +-java.lang.Object
 class-loader      +-sun.misc.Launcher$AppClassLoader@70dea4e
                     +-sun.misc.Launcher$ExtClassLoader@69260973
 classLoaderHash   70dea4e           

SC 也可以檢視已加載的類,幫助你看是否有沒有納入進來的類,尤其是在 Spring 中,可以判斷的你的依賴有沒有正确的進來。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
# 檢視JVM已加載的類資訊
[arthas@37]$ sc javax.servlet.Filter
com.example.demo.arthas.AdminFilterConfig$AdminFilter
javax.servlet.Filter
org.apache.tomcat.websocket.server.WsFilter
org.springframework.boot.web.filter.OrderedCharacterEncodingFilter
org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter
org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter
org.springframework.boot.web.filter.OrderedRequestContextFilter
org.springframework.web.filter.CharacterEncodingFilter
org.springframework.web.filter.GenericFilterBean
org.springframework.web.filter.HiddenHttpMethodFilter
org.springframework.web.filter.HttpPutFormContentFilter
org.springframework.web.filter.OncePerRequestFilter
org.springframework.web.filter.RequestContextFilter
org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
Affect(row-cnt:14) cost in 11 ms.
 
# 檢視已加載類的方法資訊
[arthas@37]$ sm java.math.RoundingMode
java.math.RoundingMode <init>(Ljava/lang/String;II)V
java.math.RoundingMode values()[Ljava/math/RoundingMode;
java.math.RoundingMode valueOf(I)Ljava/math/RoundingMode;
java.math.RoundingMode valueOf(Ljava/lang/String;)Ljava/math/RoundingMode;
Affect(row-cnt:4) cost in 6 ms.           

jad:反編譯某個類,或者反編譯某個類的某個方法。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
# 反編譯隻顯示源碼
jad --source-only com.Arthas
# 反編譯某個類的某個方法
jad --source-only com.Arthas mysql
[arthas@37]$ jad demo.MathGame
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@70dea4e
  +-sun.misc.Launcher$ExtClassLoader@69260973
Location:
/home/scrapbook/tutorial/arthas-demo.jar
/*
 * Decompiled with CFR.
 */
package demo;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class MathGame {
    private static Random random = new Random();
    public int illegalArgumentCount = 0;
    public List<Integer> primeFactors(int number) {
        if (number < 2) {
            ++this.illegalArgumentCount;
            throw new IllegalArgumentException("number is: " + number + ", need >= 2");
        }
        ArrayList<Integer> result = new ArrayList<Integer>();
        int i = 2;
        while (i <= number) {
            if (number % i == 0) {
                result.add(i);
                number /= i;
                i = 2;
                continue;
            }
            ++i;
        }
        return result;
    }
    public static void main(String[] args) throws InterruptedException {
        MathGame game = new MathGame();
        do {
            game.run();
            TimeUnit.SECONDS.sleep(1L);
        } while (true);
    }
    public void run() throws InterruptedException {
        try {
            int number = random.nextInt() / 10000;
            List<Integer> primeFactors = this.primeFactors(number);
            MathGame.print(number, primeFactors);
        }
        catch (Exception e) {
            System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage());
        }
    }
    public static void print(int number, List<Integer> primeFactors) {
        StringBuffer sb = new StringBuffer(number + "=");
        for (int factor : primeFactors) {
            sb.append(factor).append('*');
        }
        if (sb.charAt(sb.length() - 1) == '*') {
            sb.deleteCharAt(sb.length() - 1);
        }
        System.out.println(sb);
    }
}
Affect(row-cnt:1) cost in 760 ms.           

3. 方法運作相關指令

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

watch:方法執行的資料觀測

你可以通過 watch 指令,來監控某個類,監控後,運作下你的功能,複現下場景,arthas 會提供給你具體的出參和入參,幫助你排查故障。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

trace:輸出方法調用路徑,并輸出耗時

這個指令對于優化代碼非常的有用,可以看出具體每個方法執行的時間,如果是 for 循環等重複語句,還能看出 n 次循環中的最大耗時,最小耗時,和平均耗時,完美!

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

tt:官方名為時空隧道

這是我調試用的最多的指令,在你對某方法開啟 tt 後,會記錄下每一次的調用(你需要設定最大監控次數),然後你可以在任何時候會看這裡面的調用,包括出參,入參,運作耗時,是否異常等。非常強大。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

4. 線程調試相關指令

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

thread 相關指令:

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

thread -n:排列出 CPU 使用率 Top N 的線程。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

thread -b:排查阻塞的線程

我們代碼有時候設計的不好,會引發死鎖的問題,卡住整個線程執行,使用這個指令可以輕松的找到問題線程,以及問題的執行語句。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

5. 強大的 ognl 表達式

衆所周知,一般來說,表達式都是調試工具裡最強的指令,哈哈。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

在 Arthas 中你可以利用 ognl 表達式語言做很多事,比如執行某個方法,擷取某個資訊,甚至進行修改。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
[arthas@19856]$ ognl '@com.Arthas@hashSet'
@HashSet[
    @String[count1],
    @String[count2],
    @String[count29],
    @String[count28],
    @String[count0],
    @String[count27],
    @String[count5],
    @String[count26],
    @String[count6],
    @String[count25],
    @String[count3],
    @String[count24],
    
[arthas@19856]$ ognl  '@[email protected]("test")'
@Boolean[true]
[arthas@19856]$
# 檢視添加的字元
[arthas@19856]$ ognl  '@com.Arthas@hashSet' | grep test
    @String[test],
[arthas@19856]$           

甚至你可以動态更換日志輸出級别。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
$ ognl '@[email protected]'
@PrivateConfig[
    loggerConfig=@LoggerConfig[root],
    loggerConfigLevel=@Level[INFO],
    intLevel=@Integer[400],
]
$ ognl '@[email protected](@org.apache.logging.log4j.Level@ERROR)'
null
$ ognl '@[email protected]'
@PrivateConfig[
    loggerConfig=@LoggerConfig[root],
    loggerConfigLevel=@Level[ERROR],
    intLevel=@Integer[200],
  
]           

使用 Arthas 解決具體問題

1. 響應時間異常問題

工作中遇到一個優化問題,系統中一個導出表格的功能,響應時間長達 2 分鐘,雖然給内部使用,但也不能這麼誇張,用 trace 跟蹤下方法,發現是其中的手機号加解密函數占用了非常大的時間,幾千個手機号,進行了解密後加密的精彩操作,最終導緻了兩分鐘的傳回時間。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

2. 某功能 Bug 導緻伺服器傳回 500

首先通過 trace 看異常報錯的方法,之後通過 tt 排查方法,發現入參進來後,居然走錯了方法(因為多态),走到了傳回 null 的方法中,是以導緻了 NPE 空指針錯誤。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

補充

Arthas 還支援 Web Console,詳見:

alibaba.github.io/arthas/web-…
Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

相似工具

BTrace 一是個曆史比較久的工具,觀察下來 Arthas 其實和它的理念蠻相似的,相信 Arthas 也參考過 Btrace,作為一個學習樣例來開發 Arthas。詳細的優劣勢看圖:

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

其他的相似工具,還有 jvm-sandbox,有興趣的朋友可以去看看。

原理淺談

分為三個部分:

  • 啟動
  • arthas 服務端代碼分析
  • arthas 用戶端代碼分析
Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

使用了阿裡開源的元件 cli,對參數進行了解析:com.taobao.arthas.boot.Bootstrap

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

在傳入參數中沒有 pid,則會調用本地 jps 指令,列出 java 程序。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

進入主邏輯,會在使用者目錄下建立 .arthas 目錄,同時下載下傳 arthas-core 和 arthas-agent 等 lib 檔案,最後啟動用戶端和服務端。

通過反射的方式來啟動字元用戶端。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

服務端——前置準備

看服務端啟動指令可以知道 從 arthas-core.jar開始啟動,arthas-core 的 pom.xml 檔案裡面指定了 mainClass 為 com.taobao.arthas.core.Arthas,使得程式啟動的時候從該類的 main 方法開始運作。

Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
  • 首先解析入參,生成 com.taobao.arthas.core.config.Configure 類,包含了相關配置資訊;
  • 使用 jdk-tools 裡面的 VirtualMachine.loadAgent,其中第一個參數為 agent 路徑, 第二個參數向 jar 包中的 agentmain() 方法傳遞參數(此處為 agent-core.jar 包路徑和 config 序列化之後的字元串),加載 arthas-agent.jar 包;
  • 運作 arthas-agent.jar 包,指定了 Agent-Class為com.taobao.arthas.agent.AgentBootstrap。
Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中
public class Arthas {
    private Arthas(String[] args) throws Exception {
        attachAgent(parse(args));
    }
    private Configure parse(String[] args) {
        // 省略非關鍵代碼,解析啟動參數作為配置,并填充到configure對象裡面
        return configure;
    }
    private void attachAgent(Configure configure) throws Exception {
           // 省略非關鍵代碼,attach到目标程序
          virtualMachine = VirtualMachine.attach("" + configure.getJavaPid());
          virtualMachine.loadAgent(configure.getArthasAgent(),
                            configure.getArthasCore() + ";" + configure.toString());
    }
    public static void main(String[] args) {
            new Arthas(args);
    }
}           
Java 線上問題排查神器 Arthas 快速上手與原理淺談前言線上 Debug 神器 Arthas總結參考文獻Arthas 征文活動火熱進行中

服務端——監聽用戶端請求

  • 如果是 exit,logout,quit,jobs,fg,bg,kill 等直接執行;
  • 如果是其他的指令,則建立 Job,并運作;
  • 建立 Job 時,會根據具體用戶端傳遞的指令,找到對應的 Command,并包裝成 Process, Process 再被包裝成 Job;
  • 運作 Job 時,反向先調用 Process,再找到對應的 Command,最終調用 Command 的 process 處理請求。

服務端——Command 處理流程

  1. 不需要使用位元組碼增強的指令
其中 JVM 相關的使用 java.lang.management 提供的管理接口,來檢視具體的運作時資料。比較簡單,就不介紹了。
  1. 需要使用位元組碼增強的指令

位元組碼增加的指令統一繼承 EnhancerCommand 類,process 方法裡面調用 enhance 方法進行增強。調用 Enhancer 類 enhance 方法,該方法内部調用 inst.addTransformer 方法添加自定義的 ClassFileTransformer,這邊是 Enhancer 類。

Enhancer 類使用 AdviceWeaver(繼承 ClassVisitor),用來修改類的位元組碼。重寫了 visitMethod 方法,在該方法裡面修改類指定的方法。visitMethod 方法裡面使用了 AdviceAdapter(繼承了 MethodVisitor類),在 onMethodEnter 方法, onMethodExit 方法中,把 Spy 類對應的方法(ON_BEFORE_METHOD, ON_RETURN_METHOD, ON_THROWS_METHOD 等)編織到目标類的方法對應的位置。

在前面 Spy 初始化的時候可以看到,這幾個方法其實指向的是 AdviceWeaver 類的 methodOnBegin, methodOnReturnEnd 等。在這些方法裡面都會根據 adviceId 查找對應的 AdviceListener,并調用 AdviceListener 的對應的方法,比如 before,afterReturning, afterThrowing。

用戶端

用戶端代碼在 arthas-client 子產品裡面,入口類是 com.taobao.arthas.client.TelnetConsole。

主要使用 apache commons-net jar 進行 telnet 連接配接,關鍵的代碼有下面幾步:

  1. 構造 TelnetClient 對象,并初始化
  2. 構造 ConsoleReader 對象,并初始化
  3. 調用 IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, consoleReader.getOutput()) 處理各個流,一共有四個流:
    • telnet.getInputStream()
    • telnet.getOutputStream()
    • System.in
    • consoleReader.getOutput()

請求時:從本地 System.in 讀取,發送到 telnet.getOutputStream(),即發送給遠端服務端。 響應時:從 telnet.getInputStream() 讀取遠端服務端發送過來的響應,并傳遞給 consoleReader.getOutput(),即在本地控制台輸出。

關于源碼,深入下去還有很多東西需要生啃,我也沒有消化得很好,大家可以繼續閱讀詳細資料。

總結

Arthas 是一個線上 Debug 神器,小白也可以輕松上手。

一鍵安裝并啟動 Arthas

  • 方式一:通過 Cloud Toolkit 實作 Arthas 一鍵遠端診斷

Cloud Toolkit 是阿裡雲釋出的免費本地 IDE 插件,幫助開發者更高效地開發、測試、診斷并部署應用。通過插件,可以将本地應用一鍵部署到任意伺服器,甚至雲端(ECS、EDAS、ACK、ACR 和 小程式雲等);并且還内置了 Arthas 診斷、Dubbo工具、Terminal 終端、檔案上傳、函數計算 和 MySQL 執行器等工具。不僅僅有 IntelliJ IDEA 主流版本,還有 Eclipse、Pycharm、Maven 等其他版本。

推薦使用 IDEA 插件下載下傳 Cloud Toolkit 來使用 Arthas:

http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8
  • 方式二:直接下載下傳

位址:

https://github.com/alibaba/arthas

參考文獻

開源位址:

github.com/alibaba/art…

官方文檔:

alibaba.github.io/arthas

Arthas 征文活動火熱進行中

Arthas 官方正在舉行征文活動,如果你有:

  • 使用 Arthas 排查過的問題
  • 對 Arthas 進行源碼解讀
  • 對 Arthas 提出建議
  • 不限,其它與 Arthas 有關的内容

歡迎參加征文活動,還有獎品拿哦~

阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公衆号。”