作者 | 汪小哥
Arthas 對于很多 Java 開發者來說,已經不可分割了,在我們日常開發、線上問題排查中扮演了非常重要的角色。作為小開發的我,日常需要排查線上營運同學提的各種 bug、各種線上問題診斷、日常運維、線上問題優化等等。
在剛來公司時,我是比較恐懼運維任務的,代碼不熟悉、各種問題比較多...幾乎崩潰的狀态,運維的一周基本上沒有幹活,完全是全身心投入到運維的任務中,排查問題效率低下。
由于深刻體驗到了這種奔潰,我一直想改變這種狀态,直到 Arthas 的開源,讓我在這種崩潰的狀态中減輕了不少負擔,同時也讓我成為了同僚們咨詢 Arthas 排查問題的小幫手~~ 雖然使用 Arthas 特别友善,但在此過程中也遇到一些問題,作為問題咨詢小幫手也感到有點不友善,是以才造就了 Arthas idea 插件的誕生。

目前 Arthas 官方的工具還不夠簡單,需要記住一些指令,特别是一些擴充性特别強的進階文法,比如 ognl 擷取 spring context 為所欲為,watch、trace 不夠簡單,需要構造一些指令工具的資訊,是以隻需要一個能夠簡單處理字元串資訊的插件即可使用。
當在處理線上問題的時候需要最快速、最便捷的指令,是以 Idea Arthas plugin 插件還是有存在的意義和價值的。---這個是最初編寫這個插件的最核心的理由。
Arthas IDEA plugin 實踐
Arthas 的功能點非常的多(詳見下方大圖),這裡就不一一的講解了,可以參考使用文檔 ,不過最近一直在更新,使用文檔中的指令名稱可能有變化。
插件安裝
下載下傳 arthas idea 插件:
https://plugins.jetbrains.com/plugin/13581-arthas-idea- Mac:
->Preferences
Plugins
- Windows:
Settings
Plugins
Install Plugin form Disk... 導入插件
安裝之後重新開機 IDEA 就可以愉快的使用啦!
擷取 static 變量
首先要擷取 classloader 的 hash 值,然後擷取指令,這個是一個互動流程需要連貫性,後續隻要是 static 的通過 static spring context 的都需要有這個互動的流程,連貫的,都在同一個界面進行操作.粘貼執行,然後擷取結果即可。
這裡的 classloader 的 hash 值緩存起來的
最後合并的腳本如下。
ognl -x 3 '@com.wangji92.arthas.plugin.demo.controller.StaticTest@INVOKE_STATIC_NAME' -c 316bc132
反射設定 static field
通過反射進行設定 static field ,參考:
https://github.com/WangJi92/arthas-idea-plugin/issues/1填寫你想要修改的值、預設根據類型設定預設值 Str->"" Long -> 0L 等等。
ognl -x 3 '#[email protected]@class.getDeclaredField("INVOKE_STATIC_FINAL"),#modifiers=#field.getClass().getDeclaredField("modifiers"),#modifiers.setAccessible(true),#modifiers.setInt(#field,#field.getModifiers() & [email protected]@FINAL),#field.setAccessible(true),#field.set(null,"設定的值")' -c 316bc132
Spring Context Invoke
通過 spring context 進行調用 bean 的方法、字段的内容。
Static Spring Context Invoke Method Field
首頁要設定一下 static spring context 的路徑。
由于都是通過 ognl 調用 static 的 spring context 都需要 classloader,這個就是配置的 spring conetxt 的位址資訊:
@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context 參考 demo 就需要配置這個位址。
ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getBean("commonController").getRandomInteger()' -c 316bc132
Watch Spring Context Invoke Method Field
watch 這個是支援在 spring mvc 場景,通過 watch 間接的擷取 spring context,需要出發一次接口的調用,可以參考 :
https://github.com/WangJi92/arthas-idea-plugin/issues/5watch -x 3 -n 1 org.springframework.web.servlet.DispatcherServlet doDispatch '@org.springframework.web.context.support.WebApplicationContextUtils@getWebApplicationContext(params[0].getServletContext()).getBean("commonController").getRandomInteger()'
TimeTunnel Spring Context Invoke Method Field
這個是參考了橫雲斷嶺的 arthas 通過 tt 擷取 spring context 為所欲為 ,可以參考這個文檔:
https://github.com/WangJi92/arthas-idea-plugin/issues/4這裡做了些什麼?将整個連貫了起來,不需要記住參數資訊,然後對于調用的參數進行簡單的預設封裝,複雜的參數場景不支援,需要手動拼接。
記錄擷取 spring context
tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod
然後根據這個 target 擷取 spring context 調用方法
tt -w 'target.getApplicationContext().getBean("commonController").getRandomInteger()' -x 3 -i 1000
擷取某個 spring 環境變量
擷取 spring 環境變量這裡依托,static spring context ,當然這個 watch 、和 tt 擷取 spring context 的場景也是支援的。線上上環境、測試環境程式多複雜,你怎麼知道環境中的變量一定是你配置的?在 nacos 等等配置中心的場景,估計手速慢了一點點,可能就沒有上去,這個時候就有這種需求擷取目前的環境變量。選中變量,然後右鍵執行指令。由于使用靜态的 static spring context 依然需要 classloader 的值。這裡已經基本上是 arthas 的上層應用啦。
ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getEnvironment().getProperty("custom.name")' -c 316bc132
擷取全部的 spring 環境變量
比較優先級,最前面的一定優先級最高,你一定被 spring 的各種優先級順序搞暈了,那麼怎麼辦呢?arthas idea plugin 支援擷取目前的全部的環境變量,依次列印出來,這樣就可以了解優先級,特别是接入了 nacos、diamond 等遠端的配置中心,實作不一樣肯定更暈了。
參考文檔:
https://blog.csdn.net/xunjiushi9717/article/details/94050139ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#allProperties={},#standardServletEnvironment=#propertySourceIterator=#springContext.getEnvironment(),#propertySourceIterator=#standardServletEnvironment.getPropertySources().iterator(),#propertySourceIterator.{#key=#this.getName(),#allProperties.add(" "),#allProperties.add("------------------------- name:"+#key),#this.getSource() instanceof java.util.Map ?#this.getSource().entrySet().iterator.{#key=#this.key,#allProperties.add(#key+"="+#standardServletEnvironment.getProperty(#key))}:#{}},#allProperties' -c 316bc132
TimeTunnel Tt
方法執行資料的時空隧道,記錄下指定方法每次調用的入參和傳回資訊,并能對這些不同的時間下調用進行觀測(可以重新觸發,周期觸發,唯一缺點對于 ThreadLocal 資訊丢失[隐含參數]、引用對象資料變更無效),這個友善二次觸發,特别是自己調試不友善的情況下記錄下來,二次觸發、周期觸發,不過自從段嶺大神 tt 為所欲為之後都被帶偏了。這裡 arthas 插件做了一些什麼?增加了二次觸發的一些常用的指令,不讓使用者愁于記憶,整個過程更加的具有連貫性。
stack 堆棧
擷取方法從哪裡執行的調用棧(用途:源碼學習調用堆棧,了解調用流程) 這個是非常好用的功能,對于喜歡樂于排查問題的小夥伴真是福音,arthas idea 插件隻是修改的指令的內建,之前也處理自己編碼過程中的問題,源碼、問題排查技巧-Java Debug and Arthas:
https://blog.csdn.net/u012881904/article/details/104591529stack com.wangji92.arthas.plugin.demo.controller.CommonController getRandomInteger -n 5
Decompile Class Jad
反編譯方法、類的源碼, 有時候需要檢視送出的代碼是否上線呢?這個功能就非常的友好。
參考文檔:[
https://github.com/WangJi92/arth!as-idea-plugin/issues/2](
https://github.com/WangJi92/arthas-idea-plugin/issues/2)jad --source-only com.wangji92.arthas.plugin.demo.controller.CommonController getRandomInteger
watch、trace
增加了預設參數、預設展開的層級限制次數,使用者不用知道這些核心的參數,簡單的使用就好了,要使用更加的進階的自己help 一下就知道了。
watch com.wangji92.arthas.plugin.demo.controller.CommonController getRandomInteger '{params,returnObj,throwExp}' -n 5 -x 3
trace com.wangji92.arthas.plugin.demo.controller.CommonController getRandomInteger -n 5
trace -E(層級的列印 trace)
trace -E 自己構造起來非常的麻煩,通過界面操作簡化了一下,需要觀察多個類、多個方法的場景。選擇你需要的場景繼續添加即可。
trace -E com.wangji92.arthas.plugin.demo.controller.CommonController|com.wangji92.arthas.plugin.demo.service.ArthasTestService traceE|doTraceE -n 5
Heap Dump
列印堆棧,有點類似 jmap -dump:format=b,file=/temp/dump.hprof pid 下載下傳下來使用 MAT 分析即可。
heapdump /tmp/dump.hprof 列印堆棧資訊
特殊用法連結
這個必須要說一下,這個特殊用法的連結線上上自己束手無措的時候可以檢視一下,非常有用。
對于通過 spring context 調用方法說明
通過 spring context 調用複雜的方法其實是不支援的,由于這個操作起來不友善,還是必須手工處理一下。
比如這裡的 Map names 的處理方式可以借鑒一下子。
更多可以參考 demo:
https://github.com/WangJi92/arthas-plugin-demo/**
* 複雜參數調用 場景
* static spring context
* ognl -x 3 '#user=new com.wangji92.arthas.plugin.demo.controller.User(),#user.setName("wangji"),#user.setAge(27L),#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getBean("commonController").complexParameterCall(#{"wangji":#user})' -c e374b99
*
* watch get spring context 備注 需要調用一次方法
* watch -x 3 -n 1 org.springframework.web.servlet.DispatcherServlet doDispatch '#user=new com.wangji92.arthas.plugin.demo.controller.User(),#user.setName("wangji"),#user.setAge(27L),@org.springframework.web.context.support.WebApplicationContextUtils@getWebApplicationContext(params[0].getServletContext()).getBean("commonController").complexParameterCall(#{"wangji":#user})'
*
* tt get spring context ,only first get time index ok
* tt -w '#user=new com.wangji92.arthas.plugin.demo.controller.User(),#user.setName("wangji"),#user.setAge(27L),target.getApplicationContext().getBean("commonController").complexParameterCall(#{"wangji":#user})' -x 3 -i 1000
* @return
*/
@RequestMapping("complexParameterCall")
@ResponseBody
public String complexParameterCall(@RequestBody Map<String, User> names) {
if (names == null) {
return "EMPTY";
}
return names.toString();
}
總結
本文簡單介紹了 Arthas IDEA 插件的安裝與使用技巧,該插件解放了大家對于 Arthas 使用的一些記憶、機械性的重複工作,歡迎大家試用!
Arthas 官方舉行了征文活動,于 3 月 26 日—— 4 月 26 日舉辦,如果你有:
- 使用 Arthas 排查過的問題
- 對 Arthas 進行源碼解讀
- 對 Arthas 提出建議
- 不限,其它與 Arthas 有關的内容
歡迎參加征文活動,還有獎品拿哦~
點選了解詳情。
“ 阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”