前言
Arthas工具已經被我們項目組簡單的應用到了物流項目的日常運維中。物流項目之前出現過生產消費速度不一致導致內存隊列中的消息數據積壓的問題,在后來解決了問題之后,我們項目組就更加重視了對JVM的日常監控,希望能借助工具及時的發現問題。這次把這篇文章歸類到面試題中,是想表達這篇文章不是單純的操作性文章,也會進行相關的原理分析,希望能對性能監控相關的面試有所幫助。
Arthas
Arthas能做什么?
就筆者的粗淺理解來說,Arthas主要能解決兩個方面的問題:
- 線上debug
- 更方便的查看jvm相關的信息
Arthas的基本命令功能
dashboard
dashboard是一個上帝視角的數據監控面板,也是我們平時使用最多的功能,通過它可以監控線程/虛擬機和操作系統的基本信息,如下圖:
一般來說,我們會監控Runnable線程都是哪些nio線程,數量代表了活躍一直在上數的物流門數量,還會看一下當前堆的大小,是否有內存泄漏的問題,還會看一下垃圾收集器的回收次數和單次回收時間,查看下是否有異常情況出現。
sc和sm
sc命令可以查看jvm已經加載的類信息。
可以使用-d -f輸出更加詳細的信息。
比如,我們想查看RabbitMQ的一個配置類是否被正確加載進來,就可以使用sc命令實現
[arthas@24222]$ sc *.RabbitConfig
cn.net.icomp.svcmw.config.RabbitConfig
cn.net.icomp.svcmw.config.RabbitConfig$$EnhancerBySpringCGLIB$$33a99423
可以查看到除了本來的類以外,還有一個spring用CGLIB加強的動態代理類
sm可以查看加載到元數據區的方法
jad
jad可以線上反編譯class字節碼,查問題利器!官方文檔中介紹還可以配合mc和redefine熱替換class,這個目前沒有嘗試過。
比如,可以使用jad反編譯一個任務代碼
方法監控相關
筆者認為方法監控相關的命令是很有意思的命令組,值得認真學習並應用到實際工作中。
注意:方法監控類相關的指令,原理是通過字節碼增強的技術實現的,監控完畢后必須要調用stop(之前會自動reset)停止arthas或者reset重置加強過的類,否則可能會影響代碼性能。
前置知識-OGNL表達式
參考資料:OGNL 語言介紹與實踐
簡單理解,OGNL就是一個可以提供對對象中的變量訪問和設置的表達式。
watch-方法執行數據觀測
watch主要用於觀察 方法 的 入參 出參 返回值 和 異常等信息,還可以通過OGNL表達式對Arthas的通知對象中的其他相關變量進行查看。
所有可以觀察的變量如下:
public class Advice {
private final ClassLoader loader;//調用類的類加載器
private final Class<?> clazz;//調用類的class對象
private final ArthasMethod method;//調用的方法
private final Object target;//當前對象實例
private final Object[] params;//入參
private final Object returnObj;//返回對象
private final Throwable throwExp;//拋出的異常
private final boolean isBefore;//方法開始就通知的標記變量
private final boolean isThrow;//方法是否拋出異常
private final boolean isReturn;//方法是否正常返回
}
只觀測方法進入時的入參,無返回值時,可以這樣寫
watch 類名表達式 方法名表達式 "{params}" -b -x 2
只觀察方法異常時的出餐和返回值時,可以這樣寫
watch 類名表達式 方法名表達式 "{params,returnObj}" -e -x 2
只觀察方法的正常返回時的出參和返回值時,可以這樣寫
watch 類名表達式 方法名表達式 "{params,returnObj}" -s -x 2
注意:
- 如果不加 -e -b -s的參數,默認是方法異常和正常返回的兩種情況都會進行觀察。
- -x 參數代表了遍歷深度
- 還可以加 -n參數 指定執行的次數
- 還可以在觀察表達式后面 加入額外的條件表達式,進一步過濾返回結果,比如:
- 按照cost耗時進行過濾 '#cost>200'
- 按照param的第一個參數是否小於0進行過濾 "params[0]<0"
trace命令-跟蹤方法內部調用路徑並統計耗時
原理文章:Trace命令的實現原理
簡單總結下:
- trace匹配的是對應方法字節碼中的方法調用指令(invokeXXX指令,只能從當前方法開始匹配第一層子方法,無法繼續深入,如果想要監控方法調用鏈上的多層,需要用正則表達式手動匹配多個類和方法)
- 同一個線程中執行的所有方法按照調用順序可建立一個樹
代碼實踐:統計解碼方法中耗時大於1ms的方法調用
[arthas@24222]$ trace *.ProtocolService decode '#cost > 1'
方法返回:
stack-統計當前方法的調用路徑(了解當前方法都被哪些方法調用過)
其實統計的就是堆棧信息,和報異常打印的堆棧信息類似。
代碼實戰
統計decode方法的調用堆棧,只顯示兩條數據
[arthas@24222]$ stack *.ProtocolService decode -n 2
命令返回
tt-記錄下每次方法調用的環境現場
tt存在的意義在於可以完整的記錄針對當前方法的所有調用信息,包括每次調用的出入參(准確來說是出參,如果方法中對入參沒有任何修改,那么出參=入參)返回值等等。
代碼實戰:
記錄下decode方法的最近4次的方法調用,然后查看其中某一次方法調用。
[arthas@24222]$ tt -t *.ProtocolService decode -n 4
結果如下:
查看1004編號的這次方法調用。
小技巧:可以配合-x 2 把結果遍歷深度設置為2(可以觀察到返回值對象中屬性的具體值)
monitor-方法執行統計(非實時方法)
monitor命令會給出一個周期內的某方法的統計信息,包括,調用次數成功次數失敗次數,平均調用時間失敗率等等。
代碼實戰:
每10秒統計一次,decode方法相關信息
[arthas@24222]$ monitor -c 10 *.ProtocolService decode
代碼結果如下:
Arthas異常情況處理
端口被占用異常
如果觀測了其中一個應用之后,使用exit指令關閉arthas,然后再使用下面指令啟動arthas
java -jar /usr/local/arthas-packaging-3.2.0-bin/arthas-boot.jar
啟動后會提示如下:
[root@rfidapp ~]# java -jar /usr/local/arthas-packaging-3.2.0-bin/arthas-boot.jar
[INFO] arthas-boot version: 3.2.0
[INFO] Process 10704 already using port 3658
[INFO] Process 10704 already using port 8563
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 10704 org.apache.storm.LogWriter
[2]: 17459 /data/sg/ledsend-0.0.1-SNAPSHOT.V2020071501jar
[3]: 10726 org.apache.storm.daemon.worker
[4]: 13594 org.apache.catalina.startup.Bootstrap
[5]: 24222 /data/sg/svc-mw-0.0.1-SNAPSHOT-V20200618.jar
[6]: 32015 org.apache.storm.daemon.supervisor.Supervisor
[7]: 25839 /data/sg/ledcalculate-0.0.1-SNAPSHOT.jar
如果選擇了另外一個應用進行觀測,就會報下面的錯誤
[ERROR] Target process 24222 is not the process using port 3658, you will connect to an unexpected process.
[ERROR] 1. Try to restart arthas-boot, select process 10704, shutdown it first with running the 'stop' command.
[ERROR] 2. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
這個提示表明,arthas的telnet端口和http端口已經被占用,如果要使用默認的端口,就需要關閉前一個觀測的應用;要么就使用指定其他端口的方式來啟動arthas,這兩種方式都可以解決問題。