在之前的文章中,我介紹了使用 Btrace 工具進行線上代碼的debug (https://www.cnblogs.com/yougewe/p/10180483.html),其大致原理就是通過字節碼注入的方式進行輔助排查。
可以說,btrace 已經給我們的開發調試一帶來了許多的方便,我們在上面做任何想要的調試!但是,明顯, btrace 的使用還是有一定成本的,比如:安裝應用,寫調試腳本...
所以,今天我們再來看一大利器: arthas (阿爾薩斯)
arthas 官網地址:https://alibaba.github.io/arthas/
arthas 的文檔真的寫得非常棒,可以說一看就會。
但是我還是想寫一下一些自己的文檔,畢竟我們往往只會用到其中皮毛功能而已。翻閱其所有文檔也還是有點浪費了!
一、為什么要用 Arthas ?
其實,這個問題在前面已回答,而且,你為什么要用 btrace ? 同理! 具體理由如下:
- 可以很方便查到一類是從哪個 jar 包加載的?為什么會報各種類相關的 Exception?
- 懷疑自己的代碼未被部署到服務器,可以通過命令快速驗證服務器上的代碼就是本地的代碼;
- 可以直接通過 arthas 進行線上debug, 查看方法返回值以確認問題所在;
- 可以很方便嵌入自己的debug代碼,快速驗證猜想;
- 操作完成后,可以將所有debug代碼刪除,從而避免影響線上運行;
二、如何安裝?
真的是超級簡單哦;1. 先把 arthas 的 工具jar包下載下來; 2. 運行即可;
wget https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar
請問,還能更簡單嗎?
三、如何解決問題?
經過上一步安裝,並運行之后,就差不多是圖形界面操作了。
第一步,arthas 會檢測到目前正在有幾個運行中的java程序,你只需按照序號選擇就就可以了。這里你就完全置身該應用環境了。接下來就可以 do what ever you want to do 。
下面,我以幾個常用問題場景,來簡要看看其解決方案如何吧!
問題1. 我如何查找某個只知道大概的類,或者說我想確認某個類是否已被系統加載?
通過這個問題,我們可以確認一點: 我寫的代碼是否被部署了。當然,如果是對於 war 包,其實這個問題比較容易解決,因為它是一個個的 class 文件,我們可以直接使用 find 命令查找即可。然而這畢竟只是理想,事實會很復雜!
所以, arthas 怎么查找一個類?
sc *DispatcharServlet # 即可以找到需要的類全路徑,如果存在的話
sm org.springframework.web.servlet.DispatcherServlet getHandler # 查看某個方法的信息,如果存在的話
甚至我們可以使用通配符列出所有的方法:
問題2. 如何查看一個class類的具體信息?
雖然通過上面檢查類和方法是否存在,能夠解決一部分我們排查代碼的問題,但是在我們只是改動一個方法中的稍稍邏輯時,就無法通過類和方法來確認問題,此時就需要進行反編譯后進行查看了!
這事如果要我自己去干,我多半只是將jar包中的class文件,使用 javap 進行反編譯成可讀字節碼,然后很認真地核對類信息! javap 雖然已經在很大程度上減輕了我們的閱讀壓力,但仍然門檻很高。
而 使用 arthas 則簡單至極:
jad org.springframework.web.servlet.DispatcherServlet # 直接反編譯出java 源代碼,包含一此額外信息的
問題3. 如何跟蹤某個方法的返回值、入參.... ?
這個問題其實是我們在用 btrace 這樣的工具的大部分時候的初衷!雖然 trace 腳本編寫並不復雜,但是千篇一律和頻繁地更改,也給我們帶來了許多麻煩。
而這在 arthas 就是一個命令的事!
watch com.test.ob testMethod "{params, returnObj, throwExp}" -e -x 2 # 同時監控入參,返回值,及異常
如果有異常,直接打印出來,否則出入參直接監控,超級方便!
這里有支持復雜的 ognl 語法,實現更復雜邏輯,請參考: https://alibaba.github.io/arthas/watch.html
問題4. 查看最繁忙的線程,以及是否有阻塞情況發生?
查看繁忙的線程,一般我們可以通過 top 等系統命令進行查看,但是那畢竟要很多個步驟,很麻煩。
而 arthas 則直觀明了:
thread -n 3 # 查看最繁忙的三個線程棧信息 thread # 以直觀的方式展現所有的線程情況 thread -b #找出當前阻塞其他線程的線程
問題5. 如何驗證自己的代碼猜想,臨時更改代碼運行?
可能我就是認為其中有一個數字寫錯了,導致業務出錯,但是不太確認,所以想在線上直接驗證下!
基本的經驗是,在本地改了代碼后,重新打包部署,然后重啟觀察效果。但是這太慢了!
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java # 先反編譯出class源碼 # 然后使用外部工具編輯內容 mc /tmp/UserController.java -d /tmp # 再編譯成class # 最后,重新載入定義的類,就可以實時驗證你的猜測了 redefine /tmp/com/example/demo/arthas/user/UserController.class
如上,是直接更改線上代碼的方式,但是一般好像是編譯不成功的。所以,最好是 本地ide 編譯成 class 文件后,再上傳替換為好!
總之,已經完全不用重啟和發布了!這個功能真的很方便,比起重啟帶來的代價,真的是不可比的。比如,重啟時可能導致負載重分配,選主等等問題,就不是你能控制的了。
問題6. 我如何測試某個方法的性能問題?
這個問題其實我們一般不太關注,但是當性能成為問題時,則真的要關注了!平時我們使用 進入打印開始時間,退出打印一個結束時間這種方式,還是有點low了!
monitor -c 5 demo.MathGame primeFactors
以上命令直接統計 primeFactors 的響應問題:
更多參考:https://alibaba.github.io/arthas/monitor.html
問題7. 更高級的追蹤工具!
tt (TimeTumple) : 方法執行數據的時空隧道,記錄下指定方法每次調用的入參和返回信息,並能對這些不同的時間下調用進行觀測!
tt -t demo.MathGame primeFactors # 追蹤方法的響應時間情況
trace: 輸出方法內部調用路徑,並輸出方法路徑上的每個節點上耗時!
trace demo.MathGame run '#cost > 10' # 據調用耗時過濾
總之,你想要進行的調試,應該都能在這里找到實現方案。去查看文檔即可!
我覺得 arthas 有幾個非常好的理念: 1. 可視化追蹤,簡單易用; 2. 獨立classloader, 方便代碼卸載;3. 完善的文檔;
其實,我們排查問題時,總是費盡周折。然而當問題解決時,又發現簡單到不行。
阿里老話:結果很重要,過程也很重要!