做者 | 於爽 中原銀行系統研發工程師,目前在技術平臺室中間件小組從事分佈式緩存、消息隊列等相關工做。java
【Arthas 官方社區正在舉行徵文活動,參加即有獎品拿哦~點擊投稿】git
Arthas 是一款 Java應用開源診斷工具,因爲其強大的問題排查及診斷能力,自其開源以來廣受開發者的關注和使用,屢次登頂 GitHub Trending,並獲得國內多家技術媒體的推薦分享。github
Arthas 能夠經過簡單的命令交互模式,接入運行的 JVM,快速定位和診斷線上程序運行問題。在不重啓服務的狀況下,實時、動態的修改相關 code,並實時生效。具體工做原理以下:web
1. 鏈接JVM:經過attach機制,經過attach pid鏈接正在運行的JVM; 2. 查看及修改JVM字節碼:經過instrument技術對運行中的JVM附加或修改字節碼來實現加強的邏輯。
2018 年末,中原銀行開始投入人員對 Arthas 進行調研,在開源社區瞭解了主要功能,並經過閱讀 Arthas 工程大綱,明晰總體工程結構,整個執行過程以下:Arthas 底層調用 rt.jar 包的 ManagementFactory 獲取整個 jvm 內部信息,經過命令集成與後端交互,執行,返回結果,整個工程簡單清晰,容易上手。redis
2019 年初,中原銀行技術團隊開始使用推廣 Arthas 定位和診斷線上問題。算法
出於保護客戶敏感信息的嚴格要求,同時切實保障生產環境業務系統的穩定運行,咱們對 Arthas 的部分功能進行了定製化改造,對一些命令進行了隱藏:bootstrap
1. watch:watch方法能夠在沒有打印日誌的狀況下,看到方法的入參和返回值,有可能暴露客戶的敏感信息; 2. mc、redefine:mc組合rdefine能夠對代碼進行熱更新,不能知足我行生產運行管理規範要求。
同時,出於使用須要,定製化開發了 gc 等命令:小程序
1. gc:實時動態展現年輕代,年老代垃圾百分比,回收次數及耗時等狀況。
下一步,我行計劃在所有開發測試環境、部分生產環境推廣使用 Arthas 來進行問題排查與定位診斷。同時採用內部技術分享的形式向行內應用開發團隊普及推廣 Arthas 的使用。後端
除了平常問題排查使用到的方法外,Arthas 還有一些強大的功能,深受中原銀行技術團隊喜好。緩存
# target-ip 爲指定綁定的IP,若是不指定IP,Arthas只listen 127.0.0.1,因此若是想從遠程鏈接,則可使用 --target-ip參數指定listen的IP java -jar arthas-boot.jar --target-ip IP
綁定遠程訪問IP後,能夠在經過telnet或http的方式遠程鏈接 Arthas 進行問題排查。
web端訪問地址:ip:8563
/telnet訪問:ip:3658
當線上應用出現問題時,能夠將問題機器隔離起來,經過Arthas在啓動時指定target-ip,多方技術人員可同時經過遠程鏈接進行問題排查。
# 查看方法內部調用路徑,並輸出方法路徑上的每一個節點上耗時 trace ClassName methodName
使用 trace 命令能夠一層一層追蹤耗時在哪裏 ,在進行性能調優的時候十分有效。
ognl 是應用於 Java 中的一個開源的表達式語言,做用是對數據進行訪問,它擁有類型轉換、訪問對象方法、操做集合對象等功能,經過 ognl 能夠完成一些列強大的操做。
# 使用ognl調用靜態方法 ognl 「@類名@方法名(參數)」
# 使用ognl獲取靜態屬性 ognl 「@類名@屬性名」
# 查找當前類的classloader hashcode sc -d 類名 | grep classLoaderHash # 用OGNL獲取logger ognl -c ***** '@類名@logger' # 單獨設置該類的logger level ognl -c ***** '@類名@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)' #全局設置logger level ognl -c ***** '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'
gc 是我行定製化開發的功能,源自於 jstat -gcutil pid timeinterval 命令,其中 pid 能夠從 Arthas 中獲取,timeinterval(單位爲毫秒)表示 gc 每次時間間隔,默認爲 1s。
# 查看應用gc狀況(timeinterval表示間隔時間,單位毫秒,默認爲1S) gc -i timeinterval -n 5
下面記錄一些我行 Arthas 應用實踐案例(因爲行內代碼保密性要求,下文所示案例均爲場景復現所寫示例代碼)
問題描述:業務人員反饋後臺管理系統其中一個頁面響應時間很長,登陸服務器上發現 CPU 使用率較高,達到 80% 左右。
注意:Arthas 啓動時要使用與 Java 進程相同的啓動用戶。
# 啓動Arthas java -jar arthas-boot.jar [INFO] arthas-boot version: 3.2.0 [INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER. * [1]: 11360 org.gradle.launcher.daemon.bootstrap.GradleDaemon [2]: 12196 com.durian.ddp.Application # 選擇要附加的java進程編號 2 ...
啓動 Arthas,附加到對應的 java 進程,執行 thread -n 5 查看 CPU 使用率最高的 5 個線程的堆棧。
# 查看CPU使用率最高的5個線程 thread -n 5 at ***.TreeUtil.findMenuChildren(TreeUtil.java:94) at ***.TreeUtil.findMenuChildren(TreeUtil.java:92) at ***.TreeUtil.findMenuChildren(TreeUtil.java:92) at ***.TreeUtil.findMenuChildren(TreeUtil.java:92) at ***.TreeUtil.recursiveTree(TreeUtil.java:74) at ***.getOwnerDeparmentTree(DepartmentServiceImpl.java:550) ... at ***.TreeUtil.findMenuChildren(TreeUtil.java:94) at ***.TreeUtil.findMenuChildren(TreeUtil.java:92) at ***.TreeUtil.findMenuChildren(TreeUtil.java:92) at ***.TreeUtil.findMenuChildren(TreeUtil.java:92) at ***.TreeUtil.recursiveTree(TreeUtil.java:74) at ***.getOwnerDeparmentTree(DepartmentServiceImpl.java:550) ... ...
經過 thread 命令已經定位到 CPU 主要消耗在 TreeUtil
的 findMenuChildren
方法上,經過 monitor 命令查看方法的具體調用次數與耗時。
# 5s爲一個統計週期,統計TreeUtil中findMenuChildren方法的耗時 monitor -c 5 ***.TreeUtil findMenuChildren
經過 monitor 命令能夠明確該方法單次調用平均耗時爲 17 ~ 20ms,可是調用次數多,因此總體上頁面響應慢。
4. 經過 jad 命令反編譯 TreeUtil 類,查看源碼
[arthas@12196]$ jad com.durian.ddp.utils.TreeUtil ClassLoader: +-sun.misc.Launcher$AppClassLoader@18b4aac2 +-sun.misc.Launcher$ExtClassLoader@244038d0 Location: /* * Decompiled with CFR. * * Could not load the following classes: * ***.ResourceTreeVo */ ... public class TreeUtil { public static ResourceTreeVo findMenuChildren(ResourceTreeVo resourceTreeVo, List<ResourceTreeVo> treeNodes) { for (ResourceTreeVo resource : treeNodes) { if (!resourceTreeVo.getResourceId().equals(resource.getResourceParentId())) continue; if (resourceTreeVo.getChildResourceVo() == null) { resourceTreeVo.setChildResourceVo(new ArrayList()); } resourceTreeVo.getChildResourceVo().add(TreeUtil.findMenuChildren(resource, treeNodes)); } return resourceTreeVo; } public static List<ResourceTreeVo> recursiveTree(List<ResourceTreeVo> list) { ArrayList<ResourceTreeVo> trees = new ArrayList<ResourceTreeVo>(); for (ResourceTreeVo treeNode : list) { if (!StringUtils.isEmpty(treeNode.getResourceParentId())) continue; trees.add(TreeUtil.findMenuChildren(treeNode, list)); } return trees; } }
經過 jad 命令查看源碼能夠發現,此處的業務邏輯大體是經過 ResourceTreeVo
對象的 resourceParentId
字段把一個列表構建一個樹。在 findMenuChildren
方法中存在遞歸調用,並且每一次調用都須要遍歷整個 ResourceTreeVo
列表來查找子節點,時間複雜度爲 O(n)。因此在 ResourceTreeVo
列表元素比較多的時候,會很耗時。
定位到問題就方便解決了,能夠經過提早基於 list 構建一個 parentId->List<ResourceTreeVo>
的 map,每一個節點查找子節點列表的時候能夠從 map 中獲取。這樣整個構建樹的時間算法爲 O(n)。
問題描述:服務器句柄數耗盡,查看發現某個應用佔用句柄數較多。
啓動 Arthas,附加到對應的 java 進程,執行 thread 查看線程狀況。
# 查看線程狀況 thread
看到有大量的 MasterListener-mymaster-*
線程處於鏈接狀態,一直沒有釋放。
# 選擇其中一個線程查看堆棧信息 thread id
發現這些線程是由 redis.clients.JedisSentinelPool$MasterListener
產生的,那麼接下來就來查看一下 JedisSentinelPool$MasterListener
的調用狀況。
stack redis.clients.jedis.JedisSentinelPool$MasterListener
觸發一次應用請求,打印出以下堆棧信息:
經過調用鏈定位到 RedisUtil
類,發現每次請求否會觸發 RedisUtil.getJedis
方法調用 JedisSentinelPool$MasterListener
,那麼下一步咱們反編譯一下 REedisUtil
類。
# 反編譯RedisUtil類 jad cn.com.zybank.testredis.starter.RedisUtil
查看 getJedis
方法,發現 getJedis
每調用一次都會新建一個 JedisSentinelPool
。
經過分析發現,每次使用 redis 時,都會調用 getJedis
方法建立一個新的 JedisSentinelPool
,從而啓動一個 MasterListener-mymaster-*
線程,因爲該線程會一直保持監聽,不會自動釋放,故隨着應用請求的增長線程數一直增長從而致使鏈接數佔滿。
針對該問題,只需建立一個全局的 JedisSentinelPool
,每次獲取 redis 鏈接時都從該鏈接池獲取便可,這裏再也不對代碼進行展現。
我行在使用 Arthas 之前,線上問題排查每每須要查網絡、jps、jstack、jmap、jhat、jstat、hprof 等一系列操做,費時費力。目前,大多數的常見問題均可以使用 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。
隨着我行全面深化使用 Arthas,也發現了一些有待改進提高的功能,但願進一步優化完善。
截至目前,Arthas GitHub Star 已經突破 2.4 萬了,但願 Arthas 可以得到更多全球開發者的關注和喜好,也期待更多像 Arthas 同樣的國內優質項目可以開源。
Arthas 官方正在舉行徵文活動,若是你有:
歡迎參加徵文活動,還有獎品拿哦~點擊投稿
「 阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的公衆號。」