1. 程式人生 > >阿里內部的那個牛逼帶閃電的Java診斷工具終於開源了

阿里內部的那個牛逼帶閃電的Java診斷工具終於開源了

原文地址
在阿里巴巴內部,有很多自研工具供開發者使用,其中有一款工具,是幾乎每個Java開發都使用過的工具,那就是Arthas,這是一款Java診斷工具,是一款牛逼帶閃電的工具。該工具已於2018年9月份開源。

GitHub地址:https://github.com/alibaba/arthas
使用者文件:https://alibaba.github.io/arthas/

在日常開發中,你是否遇到過以下問題:

這個類從哪個 jar 包載入的?為什麼會報各種類相關的 Exception?

我改的程式碼為什麼沒有執行到?難道是我沒 commit?分支搞錯了?

遇到問題無法在線上 debug,難道只能通過加日誌再重新發布嗎?

線上遇到某個使用者的資料處理有問題,但線上同樣無法 debug,線下無法重現!

是否有一個全域性視角來檢視系統的執行狀況?

有什麼辦法可以監控到JVM的實時執行狀態?

以上問題,通通可以通過Arthas來進行問題診斷!!!是不是很好很強大。

Arthas支援JDK 6+,採用命令列互動模式,同時提供豐富的 Tab 自動補全功能,進一步方便進行問題的定位和診斷。

Arthas安裝

1、使用arthas-boot安裝

下載arthas-boot.jar,然後用java -jar的方式啟動:

wget https://alibaba.github.io/arthas/arthas-boot.jar


java -jar arthas-boot.jar

列印幫助資訊:

java -jar arthas-boot.jar -h

如果下載速度比較慢,可以使用aliyun的映象:

java -jar arthas-boot.jar --repo-mirror aliyun --use-http

2、使用as.sh安裝

Arthas 支援在 Linux/Unix/Mac 等平臺上一鍵安裝,請複製以下內容,並貼上到命令列中,敲 回車 執行即可:

curl -L https://alibaba.github.io/arthas/install.sh | sh

上述命令會下載啟動指令碼檔案

as.sh 到當前目錄,你可以放在任何地方或將其加入到 $PATH 中。

直接在shell下面執行./as.sh,就會進入互動介面。

也可以執行./as.sh -h來獲取更多引數資訊。

快速入門

  1. 啟動Demo
    wget https://alibaba.github.io/arthas/arthas-demo.jar
    java -jar arthas-demo.jar

arthas-demo是一個簡單的程式,每隔一秒生成一個隨機數,再執行質因式分解,並打印出分解結果。

  1. 啟動arthas
    在命令列下面執行:

wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar

執行該程式的使用者需要和目標程序具有相同的許可權。比如以admin使用者來執行:sudo su admin && java -jar arthas-boot.jar 或 sudo -u admin -EH java -jar arthas-boot.jar。
如果attatch不上目標程序,可以檢視~/logs/arthas/ 目錄下的日誌。
如果下載速度比較慢,可以使用aliyun的映象:java -jar arthas-boot.jar --repo-mirror aliyun --use-http
java -jar arthas-boot.jar -h 列印更多引數資訊。

選擇應用java程序:

$ $ java -jar arthas-boot.jar

[2]: 71560 arthas-demo.jar

Demo程序是第2個,則輸入2,再輸入回車/enter。Arthas會attach到目標程序上,並輸出日誌:

[INFO] Try to attach process 71560
[INFO] Attach process 71560 success.
[INFO] arthas-client connect 127.0.0.1 3658
,—. ,------. ,--------.,–. ,–. ,—. ,—.
/ O \ | .–. ‘’–. .–’| ‘–’ | / O \ ’ .-’
| .-. || ‘–’.’ | | | .–. || .-. |.-.
| | | || |\ \ | | | | | || | | |.-’ |
--'–’--' '--'–’ --'–’--'–’`-----’

wiki: https://alibaba.github.io/arthas
version: 3.0.5.20181127201536
pid: 71560
time: 2018-11-28 19:16:24

$

  1. 檢視dashboard
    輸入dashboard,按enter/回車,會展示當前程序的資訊,按ctrl+c可以中斷執行。
    $ dashboard
    ID NAME GROUP PRIORI STATE %CPU TIME INTERRU DAEMON
    17 pool-2-thread-1 system 5 WAITIN 67 0:0 false false
    27 Timer-for-arthas-dashb system 10 RUNNAB 32 0:0 false true
    11 AsyncAppender-Worker-a system 9 WAITIN 0 0:0 false true
    9 Attach Listener system 9 RUNNAB 0 0:0 false true
    3 Finalizer system 8 WAITIN 0 0:0 false true
    2 Reference Handler system 10 WAITIN 0 0:0 false true
    4 Signal Dispatcher system 9 RUNNAB 0 0:0 false true
    26 as-command-execute-dae system 10 TIMED_ 0 0:0 false true
    13 job-timeout system 9 TIMED_ 0 0:0 false true
    1 main main 5 TIMED_ 0 0:0 false false
    14 nioEventLoopGroup-2-1 system 10 RUNNAB 0 0:0 false false
    18 nioEventLoopGroup-2-2 system 10 RUNNAB 0 0:0 false false
    23 nioEventLoopGroup-2-3 system 10 RUNNAB 0 0:0 false false
    15 nioEventLoopGroup-3-1 system 10 RUNNAB 0 0:0 false false
    Memory used total max usage GC
    heap 32M 155M 1820M 1.77% gc.ps_scavenge.count 4
    ps_eden_space 14M 65M 672M 2.21% gc.ps_scavenge.time(m 166
    ps_survivor_space 4M 5M 5M s)
    ps_old_gen 12M 85M 1365M 0.91% gc.ps_marksweep.count 0
    nonheap 20M 23M -1 gc.ps_marksweep.time( 0
    code_cache 3M 5M 240M 1.32% ms)
    Runtime
    os.name Mac OS X
    os.version 10.13.4
    java.version 1.8.0_162
    java.home /Library/Java/JavaVir
    tualMachines/jdk1.8.0
    _162.jdk/Contents/Hom
    e/jre

  2. 通過sysenv命令來獲取到程序的Main Class
    $ sysenv | grep MAIN
    JAVA_MAIN_CLASS_71560 demo.MathGame

  3. 通過jad來反編繹Main Class
    $ jad demo.MathGame

ClassLoader:
±sun.misc.Launcher A p p C l a s s L o a d e r @ 3 d 4 e a c 69 + s u n . m i s c . L a u n c h e r [email protected] +-sun.misc.Launcher [email protected]

Location:
/tmp/arthas-demo.jar

/*

  • Decompiled with CFR 0_132.
    */
    package demo;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class MathGame {
private static Random random = new Random();
private int illegalArgumentCount = 0;

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();
List 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 primeFactors) {
StringBuffer sb = new StringBuffer("" + number + “=”);
Iterator iterator = primeFactors.iterator();
while (iterator.hasNext()) {
int factor = iterator.next();
sb.append(factor).append(’’);
}
if (sb.charAt(sb.length() - 1) == '
’) {
sb.deleteCharAt(sb.length() - 1);
}
System.out.println(sb);
}

public List primeFactors(int number) {
if (number < 2) {
++this.illegalArgumentCount;
throw new IllegalArgumentException("number is: " + number + “, need >= 2”);
}
ArrayList result = new ArrayList();
int i = 2;
while (i <= number) {
if (number % i == 0) {
result.add(i);
number /= i;
i = 2;
continue;
}
++i;
}
return result;
}
}

Affect(row-cnt:1) cost in 970 ms.

  1. watch
    通過watch命令來檢視demo.MathGame#primeFactors函式的返回值:
    $ watch demo.MathGame primeFactors returnObj
    Press Ctrl+C to abort.
    Affect(class-cnt:1 , method-cnt:1) cost in 107 ms.
    ts=2018-11-28 19:22:30; [cost=1.715367ms] result=null
    ts=2018-11-28 19:22:31; [cost=0.185203ms] result=null
    ts=2018-11-28 19:22:32; [cost=19.012416ms] [email protected][
    @Integer[5],
    @Integer[47],
    @Integer[2675531],
    ]
    ts=2018-11-28 19:22:33; [cost=0.311395ms] [email protected][
    @Integer[2],
    @Integer[5],
    @Integer[317],
    @Integer[503],
    @Integer[887],
    ]
    ts=2018-11-28 19:22:34; [cost=10.136007ms] [email protected][
    @Integer[2],
    @Integer[2],
    @Integer[3],
    @Integer[3],
    @Integer[31],
    @Integer[717593],
    ]
    ts=2018-11-28 19:22:35; [cost=29.969732ms] [email protected][
    @Integer[5],
    @Integer[29],
    @Integer[7651739],
    ]

  2. 退出arthas
    如果只是退出當前的連線,可以用quit或者exit命令。Attach到目標程序上的arthas還會繼續執行,埠會保持開放,下次連線時可以直接連線上。
    如果想完全退出arthas,可以執行shutdown命令。

常用命令

基礎命令

help——檢視命令幫助資訊
cls——清空當前螢幕區域
session——檢視當前會話的資訊
reset——重置增強類,將被 Arthas 增強過的類全部還原,Arthas 服務端關閉時會重置所有增強過的類
version——輸出當前目標 Java 程序所載入的 Arthas 版本號
quit——退出當前 Arthas 客戶端,其他 Arthas 客戶端不受影響
shutdown——關閉 Arthas 服務端,所有 Arthas 客戶端全部退出
keymap——Arthas快捷鍵列表及自定義快捷鍵

jvm相關

dashboard——當前系統的實時資料面板
thread——檢視當前 JVM 的執行緒堆疊資訊
jvm——檢視當前 JVM 的資訊
sysprop——檢視和修改JVM的系統屬性
New! getstatic——檢視類的靜態屬性

class/classloader相關

sc——檢視JVM已載入的類資訊
sm——檢視已載入類的方法資訊
dump——dump 已載入類的 byte code 到特定目錄
redefine——載入外部的.class檔案,redefine到JVM裡
jad——反編譯指定已載入類的原始碼
classloader——檢視classloader的繼承樹,urls,類載入資訊,使用classloader去getResource

monitor/watch/trace相關

monitor——方法執行監控
watch——方法執行資料觀測
trace——方法內部呼叫路徑,並輸出方法路徑上的每個節點上耗時
stack——輸出當前方法被呼叫的呼叫路徑
tt——方法執行資料的時空隧道,記錄下指定方法每次呼叫的入參和返回資訊,並能對這些不同的時間下呼叫進行觀測
請注意,這些命令,都通過位元組碼增強技術來實現的,會在指定類的方法中插入一些切面來實現資料統計和觀測,因此在線上、預發使用時,請儘量明確需要觀測的類、方法以及條件,診斷結束要執行 shutdown 或將增強過的類執行 reset 命令。

options

options——檢視或設定Arthas全域性開關

管道
Arthas支援使用管道對上述命令的結果進行進一步的處理,如sm org.apache.log4j.Logger | grep

grep——搜尋滿足條件的結果
plaintext——將命令的結果去除顏色
wc——按行統計輸出結果