Arthas(阿爾薩斯)是 Alibaba 開源的一款 Java 診斷工具,使用它我們可以監控和排查 Java 程式,然而它還提供了非常實用的 Java 熱更新功能。 所謂的 Java 熱更新是指在不重啟項目的情況下實現代碼的更新與替換。使用它可以實現不停機更新 Java 程式,尤其是對那些啟動非... ...
Arthas(阿爾薩斯)是 Alibaba 開源的一款 Java 診斷工具,使用它我們可以監控和排查 Java 程式,然而它還提供了非常實用的 Java 熱更新功能。
所謂的 Java 熱更新是指在不重啟項目的情況下實現代碼的更新與替換。使用它可以實現不停機更新 Java 程式,尤其是對那些啟動非常耗時的 Java 項目來說,更是效果顯著。
Arthas 的使用其實非常簡單,它為我們提供了一個 Jar 包,我們只需要把這個 Jar 下載到本地,然後運行這個 Jar 包就可以正常使用它的功能了。
Arthas 功能簡述
當你遇到以下類似問題而束手無策時,Arthas 可以幫助你解決(來自官方):
- 這個類從哪個 jar 包載入的?為什麼會報各種類相關的 Exception?
- 我改的代碼為什麼沒有執行到?難道是我沒 commit?分支搞錯了?
- 遇到問題無法線上上 debug,難道只能通過加日誌再重新發佈嗎?
- 線上遇到某個用戶的數據處理有問題,但線上同樣無法 debug,線下無法重現!
- 是否有一個全局視角來查看系統的運行狀況?
- 有什麼辦法可以監控到JVM的實時運行狀態?
- 怎麼快速定位應用的熱點,生成火焰圖?
Arthas 支持 JDK 6+,支持 Linux/Mac/Winodws,它採用命令行交互模式,同時提供豐富的 Tab 自動補全功能,進一步方便進行問題的定位和診斷。
Arthas 使用
Arthas 的使用步驟如下。
步驟一:下載 Arthas
首先,我們先把 Arthas 的 Jar 包下載到本地,它的下載地址是:
https://alibaba.github.io/arthas/arthas-boot.jar
步驟二:啟動 Arthas
我們只需要使用普通的 jar 包啟動命令:java -jar arthas-boot.jar 來啟動 Arthas 即可,啟動成功之後的運行界面如下:
如上圖所示則表示 Arthas 啟動成功。
小貼士:當我們運行 java -jar arthas-boot.jar 命令時,首先需要先切換目錄至該 jar 包的位置,才能正常的啟動 Arthas。
步驟三:運行 Arthas
當我們啟動完 Arthas 之後,根據上圖的提示,我們需要選擇一個要調試的 Java 進程,例如我們輸入“4”來監測我自己寫的一個 Java 測試程式,執行結果如下:
當出現 Arthas 的 logo 之後,表示 Arthas 正常載入了 Java 進程。
步驟四:操作 Arthas
當 Arthas 載入 Java 進程成功之後,我們就可以輸入相關的命令來查看相關的信息了。
假如我們把本地環境視為生產伺服器,我們此時需要查看某個運行的 Java 程式是否為最新版的。
在沒有 Arthas 之前,我們通常的步驟是這樣的:
- 找到相應的 jar 包(或者 war 包);
- 將 jar 包(或者 war 包)下載到本地;
- 找出相應的類進行解壓操作;
- 然後將解壓的 class 文件拖拽到 Java 編譯器(Idea 或 Eclipse)中,查看是否為最新的代碼。
但如果使用的是 Arthas,那麼我們就可以直接通過反編譯命令,將位元組碼編譯為正常的 Java 代碼,然後再確認是否為最新的代碼即可。我們只需要執行 jad 命令即可,實現示例如下:
這樣我們就可以直接來查看這個發佈的程式是否為最新版本了。
不僅如此,我們還可以使用 Arthas 來監測整個程式的運行情況,如下圖所示:
我們還可以用 Arthas 來查看一些 JVM 的相關信息,如下圖所示:
更多 Arthas 的功能,請訪問:
https://alibaba.github.io/arthas/commands.html
熱更新 Java 代碼
假如我們原來的代碼是這樣的:
package com.example; import java.util.concurrent.TimeUnit; public class App { public static void main(String[] args) throws InterruptedException { while (true) { // 每兩秒鐘列印一條信息 TimeUnit.SECONDS.sleep(3); sayHi(); } } private static void sayHi() { // 需要修改的標識 boolean flag = true; if (flag) { System.out.println("Hello,Java."); } else { System.out.println("Hello,Java中文社群."); } } }
我們現在想要把 flag 變數改為 false 就可以這樣來做:
- 使用 Arthas 的記憶體編譯工具將新的 Java 代碼編譯為位元組碼;
- 使用 Arthas 的 redefine 命令實現熱更新。
1.編譯位元組碼
首先,我們需要將新的 Java 代碼編譯為位元組碼,我們可以通過 Arthas 提供的 mc 命令實現,mc 是 Memory Compiler(記憶體編譯器)的縮寫。
實現示例如下:
[arthas@3478]$ mc /Users/admin/Desktop/App.java -d /Users/admin/Desktop Memory compiler output: /Users/admin/Desktop/com/example/App.class Affect(row-cnt:1) cost in 390 ms.
其中 -d 表示編譯文件的存放位置。
小貼士:我們也可以使用 javac App.java 生成的位元組碼,它與此步驟執行的結果相同。
2.執行熱更新
有了位元組碼文件之後,我們就可以使用 redefine 命令來實現熱更新了,實現示例如下:
[arthas@51787]$ redefine /Users/admin/Desktop/com/example/App.class redefine success, size: 1
從上述結果可以看出,熱更新執行成功,此時我們去控制台查看執行結果,如下圖所示:
這說明熱更新執行確實成功了。
Arthas 熱更新註意事項
使用熱更新功能有一些條件限制,我們只能用它來修改方法內部的一些業務代碼,如果我們出現了以下任意一種情況,那麼熱更新就會執行失敗:
- 增加類屬性(類欄位);
- 增加或刪除方法;
- 替換正在運行的方法。
最後一條我們需要單獨說明一下,假如我們把上面的示例改為如下代碼:
package com.example; import java.util.concurrent.TimeUnit; public class App { public static void main(String[] args) throws InterruptedException { while (true) { // 每兩秒鐘列印一條信息 TimeUnit.SECONDS.sleep(3); boolean flag = true; if (flag) { System.out.println("Hello,Java."); } else { System.out.println("Hello,Java中文社群."); } } } }
那麼此時我們再進行熱更新操作修改 flag 的值,那麼就會執行失敗,因為我們替換的是正在運行中的方法,而我們正常示例中的代碼之所以能成功,是因為我們在 while無線迴圈中調用了另一個方法,而那個方法是被間歇性使用的,因此可以替換成功。
總結
本文我們講了 Arthas 的概念以及具體的使用流程,Arthas 其實就是一個普通的 Java 程式,我們可以使用 java -jar arthas-boot.jar 來啟動它,然後再選擇我們要操作的 Java 進程,這樣就可以實現狀態監控和其他操作。
文章的後半部分,我們介紹了 Arthas 的熱更新功能,而熱更新本質上只需要使用一個 redefine 命令來載入新的位元組碼文件就可以實現熱更新了,但需要註意熱更新不能替換正在運行的方法,它只能修改方法內部的業務代碼,如果修改了類欄位或者是更改了類方法,那麼熱更新就會執行失敗。
分享
最後小編整理了一份Java相關的資料,需要的小伙伴可以加我微信即可免費領取!
放一些大概截圖,感興趣的小伙伴可以收著。