天天看點

Arthas watch 指令使用指南Arthas watch 指令使用指南Arthas 征文活動火熱進行中

作者 | Agentd

Arthas watch 指令使用指南

Arthas 是我很喜歡的一款 Java 領域的開發調試工具。

每次測試遇到問題的時候,當别人為了加一條日志而重發代碼,我都會欣慰地拿出我的 Arthas 并且告訴他們:少年,你不用再為了加日志就重發代碼而煩惱了。Arthas,你值得擁有。

這次我要介紹的是我使用最多的一個功能:

watch

。Arthas 功能雖多,但我最喜歡的還是這一個。使用

watch

之後,我再也不用為了觀察函數調用而加日志了。

Arthas 是什麼

Arthas

官網是這麼介紹自己的:

Arthas 是 Alibaba 開源的 Java 診斷工具,深受開發者喜愛。 當你遇到以下類似問題而束手無策時,Arthas 可以幫助你解決:
  1. 這個類從哪個 jar 包加載的?為什麼會報各種類相關的 Exception?
  2. 我改的代碼為什麼沒有執行到?難道是我沒 commit?分支搞錯了?
  3. 遇到問題無法線上上 debug,難道隻能通過加日志再重新釋出嗎?
  4. 線上遇到某個使用者的資料處理有問題,但線上同樣無法 debug,線下無法重制!
  5. 是否有一個全局視角來檢視系統的運作狀況?
  6. 有什麼辦法可以監控到 JVM 的實時運作狀态?
  7. 怎麼快速定位應用的熱點,生成火焰圖?

一鍵安裝并啟動 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

curl -O https://alibaba.github.io/arthas/arthas-boot.jar && java -Dfile.encoding=UTF-8 -jar arthas-boot.jar
複制代碼           

稍微解釋一下上面這條 shell 指令。指令分為兩部分,

&&

之前的部分是下載下傳 Arthas,之後的部分是啟動 Arthas。

你可能會疑惑下載下傳檔案為什麼不用

wget

而是用

curl

?這是因為有些伺服器是沒有預裝

wget

的,但是基本都預裝了

curl

。如果你的伺服器預裝了

wget

的話完全可以把 'curl' 改成

wget

如果使用 wget 的話指令可以改成:

# wget 版指令
wget https://alibaba.github.io/arthas/arthas-boot.jar && java -Dfile.encoding=UTF-8 -jar arthas-boot.jar
複制代碼           

另外一個需要解釋的點是

-Dfile.encoding=UTF-8

,這個 Java 設定是為了讓 Arthas 輸出中文的時候不會亂碼,這一點可以看一下我以前的文章

由 Arthas 中文亂碼引發的 Java 預設編碼思考

Arthas watch 指令

watch

讓你能友善地觀察到指定方法的調用情況。能觀察到的範圍為:

傳回值

抛出異常

入參

(還能觀察執行函數的對象本身,不知道為什麼官方介紹的時候沒說這個」,通過編寫 OGNL 表達式進行對應變量的檢視。

# watch -h
# USAGE
watch [-b] [-e] [-x <value>] [-f] [-h] [-n <value>] [-E] [-M <value>] [-s] class-pattern method-pattern express [condition-express]
複制代碼           

1. 觀察方法傳回結果

returnObj

使用方式看着複雜,其實很簡單。來個最簡單的示例: 假設我們要觀察下面這段代碼中字元串的

contains

方法。

public class App {
    public static void main(String[] args) throws IOException {
        String hello = "Hello Arthas";
        while (true) {
            boolean contains = StringUtils.contains(hello, "Arthas");
            System.out.println(contains);
        }
    }
}
複制代碼           

可以使用如下語句:

## 觀察 contains 傳回結果
[arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains returnObj -n 3
# Press Q or Ctrl+C to abort.
# Affect(class-cnt:1 , method-cnt:2) cost in 68 ms.
# ts=2020-05-02 16:46:04; [cost=2.424254ms] result=@Boolean[true]
# ts=2020-05-02 16:46:05; [cost=0.21033ms] result=@Boolean[true]
# ts=2020-05-02 16:46:06; [cost=0.165514ms] result=@Boolean[true]
複制代碼           

-n 3

表示隻執行三次,這參數挺常用,不然很容易被輸出刷屏。

2. 過濾不關心的調用

condition-express

顯然,真實的案例肯定不會如上面的示例那麼簡單。 真實的服務代碼中,肯定不止一個地方調用了 String 的

contains

方法。我們需要把無關的調用過濾掉。

## 觀察 contains 傳回結果,并且過濾掉無關調用
[arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains returnObj 'params[1]=="Arthas"'
# Press Q or Ctrl+C to abort.
# Affect(class-cnt:1 , method-cnt:2) cost in 29 ms.
# ts=2020-05-02 16:48:50; [cost=0.331109ms] result=@Boolean[true]
# ts=2020-05-02 16:48:51; [cost=0.175224ms] result=@Boolean[true]
# ts=2020-05-02 16:48:52; [cost=0.138984ms] result=@Boolean[true]
複制代碼           

入參是一個很容易把不同調用區分開的方法,通過

params[1]=="Arthas"

這個

condition-express

,我們可以隻保留第二個入參是

Arthas

的函數調用。

3. 同時觀察入參和結果

[arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains {params,returnObj} 'params[1]=="Arthas"'
# Press Q or Ctrl+C to abort.
# Affect(class-cnt:1 , method-cnt:2) cost in 33 ms.
# ts=2020-05-02 16:51:27; [cost=0.507486ms] result=@ArrayList[
#     @Object[][isEmpty=false;size=2],
#     @Boolean[true],
# ]
複制代碼           

通過

{}

把字段包起來,可以同時觀察想觀察的字段。可以注意到一個點,

params

是一個數組,但是列印

params

的時候并沒有把具體内容列印出來,這個時候可以使用

-x 2

來指定列印對象的屬性周遊深度。

arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains  {params,returnObj} 'params[1]=="Arthas"' -x 2
# Press Q or Ctrl+C to abort.
# Affect(class-cnt:1 , method-cnt:2) cost in 35 ms.
# ts=2020-05-02 16:51:33; [cost=0.391218ms] result=@ArrayList[
#     @Object[][
#         @String[Hello Arthas],
#         @String[Arthas],
#     ],
#     @Boolean[true],
# ]
複制代碼           

4. 給大家來幾個我實際用到的例子

在陌陌做動态推薦開發的時候,測試時經常會遇到檢視某個使用者是否開啟了相應的業務開關,經常就會需要檢視某個實驗開關是否開啟。

## 檢視陌陌使用者 1234567 是否開啟 ElasticSearch 開關
watch com.momo.Experiment enableElasticSearch returnObj 'target.momoId=="1234567"'
# ts=2020-05-02 20:09:46; [cost=24.443527ms] result=@Boolean[true]
複制代碼           

我還經常會根據入參的陌陌使用者 id 進行判斷,檢視傳回結果或者異常:

## 檢視 MorecControlFlow 類的 process 方法傳回結果
## process 方法第一個參數的 momoId 屬性值為 “123454567” 才生效
## 類路徑和陌陌号都非真實資料
watch com.momo.MorecControlFlow process returnObj 'params[0].momoId=="123454567"'
# ts=2019-03-18 21:09:46; [cost=264.434972ms] result=@Boolean[true]
## 檢視 IMorecShuffler 類的 shuffle 抛的異常
## process 方法第一個參數的 momoId 屬性值為 “123454567” 才生效
watch com.momo.plugins.shuffler.IMorecShuffler shuffle throwExp 'params[0].morecRequest.momoId=="123454567"'
# ts=2019-03-27 20:54:29; [cost=46.642339ms] result=java.lang.IndexOutOfBoundsException: Index: 12, Size: 11
    at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:665)
    at java.util.ArrayList.add(ArrayList.java:477)
    at com.momo.plugin.shuffler.RoomShuffler.shuffle(RoomShuffler:45)
複制代碼           

一些小提示

上面我隻是列了一下常用的觀察方式和參數,watch 支援的指令還有很多,你可以檢視 Arthas 的 watch 指令

官方文檔

還可以通過啟動 arthas 指令之後使用

watch -h

檢視。

使用 Arthas 的過程中很多人會覺得擷取類的全限定名很費勁,其實這個可以通過 Idea 的

Copy Refrence

快捷鍵解決。我自己定義的快捷鍵是

⌥⇧⌘C

還有一點就是寫代碼的時候最好把代碼拆細,盡量把小功能也封裝成單獨的函數,等你需要使用 Arthas 觀察函數調用的時候,你會回來感謝自己的。

Arthas 征文活動火熱進行中

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

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

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

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