【Arthas 官方社區正在舉行徵文活動,參加即有獎品拿哦~點擊投稿】html
做者 | 楊楨棟,筆名叫蠻三刀把刀,是一名一線互聯網碼農,留美訪學一年,主要關注後端開發,數據安全,爬蟲,物聯網,邊緣計算等方向。java
當你興沖沖地開始運行本身的 Java 項目時,你是否遇到過以下問題:mysql
之前,你碰到這些問題,解決的辦法大可能是,修改代碼,從新上線。可是在大公司裏,上線的流程是很是繁瑣的,若是爲了多加一行日誌而從新發布版本,無疑是很是折騰人的。git
如今,咱們有了更爲優雅的線上調試方法 - 來自阿里巴巴開源的 Arthas。程序員
下圖是 Arthas 文檔中對於爲何要使用它的描述,我進行了精簡:github
好了,前言已經超過字數了,哈哈,在本篇文章裏,你可以瞭解:web
相信我,Arhas 絕對是你提高效率的利器,適合各類階段的開發者,尤爲適合我這種剛入門的新人(每天上班寫 Bug 的人)。你不該該有這種東西是高階程序員才應該去使用的思想,放心大膽的去用吧!spring
命令的詳細文檔請參考:alibaba.github.io/arthas/comm…sql
快速啓動它,你只須要兩行命令:apache
wget https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar
隨後,在界面出現的進程中,選擇你的程序序號,好比 1
這樣你就進入了 arthas 的控制檯。
Arthas 有以下功能:
當前系統的實時數據面板,按 ctrl+c 退出;
當運行在 Ali-tomcat 時,會顯示當前 tomcat 的實時信息,如 HTTP 請求的 qps, rt, 錯誤數, 線程池信息等等。
經過這些,你能夠對於整個程序進程有個直觀的數據監控。
SC:查看 JVM 已加載的類信息
經過 SC 咱們能夠看到咱們這個類的詳細信息,包括是從哪一個 jar 包讀取的,他是否是接口/枚舉類等,甚至包括他是從哪一個類加載器加載的。
上圖中代碼:
[arthas@37]$ sc -d *MathGame class-info demo.MathGame code-source /home/scrapbook/tutorial/arthas-demo.jar name demo.MathGame isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name MathGame modifier public annotation interfaces super-class +-java.lang.Object class-loader +-sun.misc.Launcher$AppClassLoader@70dea4e +-sun.misc.Launcher$ExtClassLoader@69260973 classLoaderHash 70dea4e
SC 也能夠查看已加載的類,幫助你看是否有沒有歸入進來的類,尤爲是在 Spring 中,能夠判斷的你的依賴有沒有正確的進來。
上圖中代碼:
# 查看JVM已加載的類信息 [arthas@37]$ sc javax.servlet.Filter com.example.demo.arthas.AdminFilterConfig$AdminFilter javax.servlet.Filter org.apache.tomcat.websocket.server.WsFilter org.springframework.boot.web.filter.OrderedCharacterEncodingFilter org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter org.springframework.boot.web.filter.OrderedRequestContextFilter org.springframework.web.filter.CharacterEncodingFilter org.springframework.web.filter.GenericFilterBean org.springframework.web.filter.HiddenHttpMethodFilter org.springframework.web.filter.HttpPutFormContentFilter org.springframework.web.filter.OncePerRequestFilter org.springframework.web.filter.RequestContextFilter org.springframework.web.servlet.resource.ResourceUrlEncodingFilter Affect(row-cnt:14) cost in 11 ms. # 查看已加載類的方法信息 [arthas@37]$ sm java.math.RoundingMode java.math.RoundingMode <init>(Ljava/lang/String;II)V java.math.RoundingMode values()[Ljava/math/RoundingMode; java.math.RoundingMode valueOf(I)Ljava/math/RoundingMode; java.math.RoundingMode valueOf(Ljava/lang/String;)Ljava/math/RoundingMode; Affect(row-cnt:4) cost in 6 ms.
jad:反編譯某個類,或者反編譯某個類的某個方法。
上圖中代碼:
# 反編譯只顯示源碼 jad --source-only com.Arthas # 反編譯某個類的某個方法 jad --source-only com.Arthas mysql [arthas@37]$ jad demo.MathGame ClassLoader: +-sun.misc.Launcher$AppClassLoader@70dea4e +-sun.misc.Launcher$ExtClassLoader@69260973 Location: /home/scrapbook/tutorial/arthas-demo.jar /* * Decompiled with CFR. */ package demo; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; public class MathGame { private static Random random = new Random(); public int illegalArgumentCount = 0; public List<Integer> primeFactors(int number) { if (number < 2) { ++this.illegalArgumentCount; throw new IllegalArgumentException("number is: " + number + ", need >= 2"); } ArrayList<Integer> result = new ArrayList<Integer>(); int i = 2; while (i <= number) { if (number % i == 0) { result.add(i); number /= i; i = 2; continue; } ++i; } return result; } 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() / 10000; List<Integer> 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<Integer> primeFactors) { StringBuffer sb = new StringBuffer(number + "="); for (int factor : primeFactors) { sb.append(factor).append('*'); } if (sb.charAt(sb.length() - 1) == '*') { sb.deleteCharAt(sb.length() - 1); } System.out.println(sb); } } Affect(row-cnt:1) cost in 760 ms.
watch:方法執行的數據觀測
你能夠經過 watch 指令,來監控某個類,監控後,運行下你的功能,復現下場景,arthas 會提供給你具體的出參和入參,幫助你排查故障。
trace:輸出方法調用路徑,並輸出耗時
這個指令對於優化代碼很是的有用,能夠看出具體每一個方法執行的時間,若是是 for 循環等重複語句,還能看出 n 次循環中的最大耗時,最小耗時,和平均耗時,完美!
tt:官方名爲時空隧道
這是我調試用的最多的指令,在你對某方法開啓 tt 後,會記錄下每一次的調用(你須要設置最大監控次數),而後你能夠在任什麼時候候會看這裏面的調用,包括出參,入參,運行耗時,是否異常等。很是強大。
thread 相關命令:
thread -n:排列出 CPU 使用率 Top N 的線程。
thread -b:排查阻塞的線程
咱們代碼有時候設計的很差,會引起死鎖的問題,卡住整個線程執行,使用這個指令能夠輕鬆的找到問題線程,以及問題的執行語句。
衆所周知,通常來講,表達式都是調試工具裏最強的指令,哈哈。
在 Arthas 中你能夠利用 ognl 表達式語言作不少事,好比執行某個方法,獲取某個信息,甚至進行修改。
[arthas@19856]$ ognl '@com.Arthas@hashSet' @HashSet[ @String[count1], @String[count2], @String[count29], @String[count28], @String[count0], @String[count27], @String[count5], @String[count26], @String[count6], @String[count25], @String[count3], @String[count24], [arthas@19856]$ ognl '@com.Arthas@hashSet.add("test")' @Boolean[true] [arthas@19856]$ # 查看添加的字符 [arthas@19856]$ ognl '@com.Arthas@hashSet' | grep test @String[test], [arthas@19856]$
甚至你能夠動態更換日誌輸出級別。
$ ognl '@com.lz.test@LOGGER.logger.privateConfig' @PrivateConfig[ loggerConfig=@LoggerConfig[root], loggerConfigLevel=@Level[INFO], intLevel=@Integer[400], ] $ ognl '@com.lz.test@LOGGER.logger.setLevel(@org.apache.logging.log4j.Level@ERROR)' null $ ognl '@com.lz.test@LOGGER.logger.privateConfig' @PrivateConfig[ loggerConfig=@LoggerConfig[root], loggerConfigLevel=@Level[ERROR], intLevel=@Integer[200], ]
工做中遇到一個優化問題,系統中一個導出表格的功能,響應時間長達 2 分鐘,雖然給內部使用,但也不能這麼誇張,用 trace 跟蹤下方法,發現是其中的手機號加解密函數佔用了很是大的時間,幾千個手機號,進行了解密後加密的精彩操做,最終致使了兩分鐘的返回時間。
首先經過 trace 看異常報錯的方法,以後經過 tt 排查方法,發現入參進來後,竟然走錯了方法(由於多態),走到了返回 null 的方法中,因此致使了 NPE 空指針錯誤。
Arthas 還支持 Web Console,詳見:alibaba.github.io/arthas/web-…
BTrace 一是個歷史比較久的工具,觀察下來 Arthas 其實和它的理念蠻類似的,相信 Arthas 也參考過 Btrace,做爲一個學習樣例來開發 Arthas。詳細的優劣勢看圖:
其餘的類似工具,還有 jvm-sandbox,有興趣的朋友能夠去看看。
分爲三個部分:
使用了阿里開源的組件 cli,對參數進行了解析:com.taobao.arthas.boot.Bootstrap
在傳入參數中沒有 pid,則會調用本地 jps 命令,列出 java 進程。
進入主邏輯,會在用戶目錄下創建 .arthas 目錄,同時下載 arthas-core 和 arthas-agent 等 lib 文件,最後啓動客戶端和服務端。
經過反射的方式來啓動字符客戶端。
看服務端啓動命令能夠知道 從 arthas-core.jar開始啓動,arthas-core 的 pom.xml 文件裏面指定了 mainClass 爲 com.taobao.arthas.core.Arthas,使得程序啓動的時候從該類的 main 方法開始運行。
上圖中代碼:
public class Arthas { private Arthas(String[] args) throws Exception { attachAgent(parse(args)); } private Configure parse(String[] args) { // 省略非關鍵代碼,解析啓動參數做爲配置,並填充到configure對象裏面 return configure; } private void attachAgent(Configure configure) throws Exception { // 省略非關鍵代碼,attach到目標進程 virtualMachine = VirtualMachine.attach("" + configure.getJavaPid()); virtualMachine.loadAgent(configure.getArthasAgent(), configure.getArthasCore() + ";" + configure.toString()); } public static void main(String[] args) { new Arthas(args); } }
其中 JVM 相關的使用 java.lang.management 提供的管理接口,來查看具體的運行時數據。比較簡單,就不介紹了。
字節碼增長的命令統一繼承 EnhancerCommand 類,process 方法裏面調用 enhance 方法進行加強。調用 Enhancer 類 enhance 方法,該方法內部調用 inst.addTransformer 方法添加自定義的 ClassFileTransformer,這邊是 Enhancer 類。Enhancer 類使用 AdviceWeaver(繼承 ClassVisitor),用來修改類的字節碼。重寫了 visitMethod 方法,在該方法裏面修改類指定的方法。visitMethod 方法裏面使用了 AdviceAdapter(繼承了 MethodVisitor類),在 onMethodEnter 方法, onMethodExit 方法中,把 Spy 類對應的方法(ON_BEFORE_METHOD, ON_RETURN_METHOD, ON_THROWS_METHOD 等)編織到目標類的方法對應的位置。
在前面 Spy 初始化的時候能夠看到,這幾個方法其實指向的是 AdviceWeaver 類的 methodOnBegin, methodOnReturnEnd 等。在這些方法裏面都會根據 adviceId 查找對應的 AdviceListener,並調用 AdviceListener 的對應的方法,好比 before,afterReturning, afterThrowing。
客戶端代碼在 arthas-client 模塊裏面,入口類是 com.taobao.arthas.client.TelnetConsole。
主要使用 apache commons-net jar 進行 telnet 鏈接,關鍵的代碼有下面幾步:
調用 IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, consoleReader.getOutput()) 處理各個流,一共有四個流:
請求時:從本地 System.in 讀取,發送到 telnet.getOutputStream(),即發送給遠程服務端。 響應時:從 telnet.getInputStream() 讀取遠程服務端發送過來的響應,並傳遞給 consoleReader.getOutput(),即在本地控制檯輸出。
關於源碼,深刻下去還有不少東西須要生啃,我也沒有消化得很好,你們能夠繼續閱讀詳細資料。
Arthas 是一個線上 Debug 神器,小白也能夠輕鬆上手。
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。
開源地址:
github.com/alibaba/art…
官方文檔:
alibaba.github.io/arthas
Arthas 官方正在舉行徵文活動,若是你有:
歡迎參加徵文活動,還有獎品拿哦~點擊投稿
「 阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的公衆號。」