阿里開源那個(gè)牛哄哄問題排查工具竟然不會用?最佳實(shí)踐來了!
**一、入門步驟
1. 安裝** 上述命令會下載啟動腳本文件 as.sh 到當(dāng)前目錄: ``` curl -L https://alibaba.github.io/arthas/install.sh | sh ``` or as.sh 啟動: ``` curl -sk https://arthas.gitee.io/arthas-boot.jar -o ~/.arthas-boot.jar && echo "alias as.sh='java -jar ~/.arthas-boot.jar --repo-mirror aliyun --use-http'" >> ~/.bashrc && source ~/.bashrc ```
2. 在線教程體驗(yàn)** 當(dāng)然也可以自己本地體驗(yàn)一下~自己通過下載一個(gè) arthas-idea-plugin 的體驗(yàn) demo 直接本地上手。 ``` github.com/WangJi92/ar… ``` **全局命令說明** - -x 是展示結(jié)果屬性遍歷深度,默認(rèn)為 1 - -n 是執(zhí)行的次數(shù) ,q 退出 - -c classloader 的hash值 - 退出 q ,關(guān)閉 stop
3. 了解最常用的trace、watch的功能** watch和trace 是arthas 診斷中對于開發(fā)人員解決線上的問題最常用的功能!
trace
基本示例** ``` trace com.wangji92.arthas.plugin.demo.controller.CommonController getRandomInteger -n 5 '1==1' ``` - 性能優(yōu)化~ - 調(diào)用的這個(gè)方法,走的具體流程是咋樣的!可以通過調(diào)用鏈看出來。 - 有異常了可以查看異常的堆棧。 **高級的功能** trace命令只會trace匹配到的函數(shù)里的子調(diào)用,并不會向下trace多層。因?yàn)閠race是代價(jià)比較貴的,多層trace可能會導(dǎo)致最終要trace的類和函數(shù)非常多。 ``` trace -E xxxClassA|xxxClassB method1 | method2 trace -E com.wangji92.arthas.plugin.demo.controller.CommonController|com.wangji92.arthas.plugin.demo.service.ArthasTestService traceE|doTraceE -n 5 '1==1' ```
watch** wathc 從字面上理解就是觀察值的信息,可以查看入?yún)?、返回值、異常、可以?zhí)行表達(dá)式獲取靜態(tài)變量、target.xxx調(diào)用目標(biāo)實(shí)施的字段、方法等等都行~只要你想得到?jīng)]有做不到的~ **基本示例** ``` watch com.wangji92.arthas.plugin.demo.controller.CommonController traceE '{params,returnObj,throwExp}' -n 5 -x 3 '1==1' ```
4、arthas 表達(dá)式核心變量** ``` public class Advice { private final ClassLoader loader; private final Class clazz; 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; // getter/setter } ``` 從watch 和 trace 中看到 后面的 '1==1' 執(zhí)行的是一個(gè)條件表達(dá)式 當(dāng)值為true 的時(shí)候通過執(zhí)行了一個(gè)ognl 表達(dá)式 ,watch 觀察 params,returnObj,throwExp 入?yún)?、返回值、是否異?這個(gè)也是一個(gè)表達(dá)式,那么這個(gè)到底是咋回事? **spring el 表達(dá)式** 沒有學(xué)習(xí)過ognl 使用多年的spring 一定知道他的el 表達(dá)式,el 表達(dá)式中也有一種概念叫做【Context 上下文,和表達(dá)式】 如下所示,因?yàn)橛辛藄imple這個(gè)上下文 才能解析 "booleanList" 這個(gè)腳本的含義~ 這個(gè)很熟悉,很好理解,那么ognl 表達(dá)式一樣不難了。 ``` class Simple { public List booleanList = new ArrayList(); } Simple simple = new Simple(); simple.booleanList.add(true); StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple); // false is passed in here as a string. SpEL and the conversion service will // correctly recognize that it needs to be a Boolean and convert it parser.parseExpression("booleanList").setValue(simpleContext, "false"); // b will be false Boolean b = simple.booleanList.get(0); ```
ognl 表達(dá)式** arthas 也是一樣的,只是使用了一個(gè)叫做ognl的腳本,核心變量就是他的上下文,可以直接獲取到這些字段。watch 觀察的這幾個(gè)字段 params,returnObj,throwExp 也就是我們所謂的上下文的概念,觀察參數(shù)、返回值、和異常的信息。 如下是arthas 源碼中 表達(dá)式評估和watch 觀察值執(zhí)行的代碼!Advice 就是一個(gè)上下文,這里還增加了一個(gè)變量 const。知道了這些那不是很簡單?? ``` com.taobao.arthas.core.advisor.ReflectAdviceListenerAdapter#isConditionMet /
判斷條件是否滿足,滿足的情況下需要輸出結(jié)果 * @param conditionExpress 條件表達(dá)式 * @param advice 當(dāng)前的advice對象 * @param cost 本次執(zhí)行的耗時(shí) * @return true 如果條件表達(dá)式滿足 */ protected boolean isConditionMet(String conditionExpress, Advice advice, double cost) throws ExpressException { return StringUtils.isEmpty(conditionExpress) || ExpressFactory.threadLocalExpress(advice).bind(Constants.COST_VARIABLE, cost).is(conditionExpress); } protected Object getExpressionResult(String express, Advice advice, double cost) throws ExpressException { return ExpressFactory.threadLocalExpress(advice) .bind(Constants.COST_VARIABLE, cost).get(express); } ```
表達(dá)式實(shí)踐
arthas 群經(jīng)常有人問重載方法如何判斷,無非就是評估條件? 參數(shù)的個(gè)數(shù)、第一個(gè)參數(shù)是什么?返回值的類型等等都可以作為你評估的條件。如下的watch 前面的一段是觀察的值、后面這一段是表達(dá)式評估 ,滿足了條件才執(zhí)行。 入?yún)㈤L度大于0 ``` watch com.wangji92.arthas.plugin.demo.controller.CommonController traceE '{params,returnObj,throwExp}' -n 5 -x 3 'params.length >0' ``` 返回值為String 且長度大于5 ``` watch com.wangji92.arthas.plugin.demo.controller.CommonController traceE '{params,returnObj,throwExp}' -n 5 -x 3 'returnObj instanceof java.lang.String && returnObj.length>5' ```
條件表達(dá)式+異步任務(wù)
- 只有特定的場景才會有bug ,如何排查bug? - 一天只出現(xiàn)一兩次如何解決? 條件表達(dá)式主要是用來過濾使用,比如某些場景只是在特定的參數(shù)才會出現(xiàn),肯能會花費(fèi)很多的時(shí)間去等待,這個(gè)時(shí)候可以使用條件表達(dá)式過濾 +異步任務(wù) **5、ognl 表達(dá)式** arthas.gitee.io/ognl/1.html 從上面看,ognl 在watch、trace上面無所不能啊,其實(shí)還有tt 也是 使用ognl 表達(dá)式執(zhí)行邏輯的。 @xxxClas@xxxStaticField 是靜態(tài)變量的語法糖 ognl的,好好看一下官方的文檔。 靜態(tài)變量由于 一個(gè)jvm 中可能被多個(gè)classloader加載,jvm 認(rèn)定為一個(gè)實(shí)例是一個(gè)classloader加載哦,所以需要知道當(dāng)前靜態(tài)類的hash 值(sc -d com.wangji92.arthas.plugin.demo.controller.StaticTest)可以通過這個(gè)命令獲取。 ``` ognl -x 3 '@com.wangji92.arthas.plugin.demo.controller.StaticTest@INVOKE_STATIC_DOUBLE' -c e374b99 ``` **調(diào)用spring 方法?** watch 執(zhí)行ognl 語法中獲取spring context 然后進(jìn)行調(diào)用bean的方法 ``` watch -x 3 -n 1 org.springframework.web.servlet.DispatcherServlet doDispatch '@org.springframework.web.context.support.WebApplicationContextUtils@getWebApplicationContext(params.getServletContext()).getBean("commonController").getRandomInteger()' ``` ognl 執(zhí)行靜態(tài)的一個(gè)spring context 然后調(diào)用bean 的方法 ``` ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getBean("commonController").getRandomInteger()' -c e374b99 ``` 有沒有起飛的感覺,無所不能!前提是你要掌握一些ognl的一些簡單的語法! **6、完畢** 對于線上排查問題,我感覺這幾個(gè)命令夠你用了,還有一些其他的反編譯、火焰圖、.. 時(shí)間隧道、logger 等級修改,jvm環(huán)境信息等等感覺是有頻率都沒有上面的高,畢竟jvm信息有專門的監(jiān)控~即使沒有arthas 你也可以找到更好的工具去分析堆棧,jvm故障。 完了?
???這么多命令 記不住啊 還有一些高級的ognl的語法涼了... 讓你獲取一下所有的spring的環(huán)境變量咋辦?trace、watch 這兩個(gè)命令我還沒有體驗(yàn)夠呢?更加高級的讓我如何是好啊!好了,請看下文。
二、進(jìn)階** **前提** 前提是你對于arthas 有了大概的理解,基本上的命令都有點(diǎn)概念了,ognl 簡單的語法能夠看懂了.. 簡單的條件表達(dá)式會用了。 之前我們所過arthas的命令這么多 要記住小本本少不了??!難受想哭~ 不要急,汪小哥來給你解決問題! 目前Arthas 官方的工具還不夠足夠的簡單,需要記住一些命令,特別是一些擴(kuò)展性特別強(qiáng)的高級語法,比如ognl獲取spring context 為所欲為,watch、trace 不夠簡單,需要構(gòu)造一些命令工具的信息,因此只需要一個(gè)能夠簡單處理字符串信息的插件即可使用。當(dāng)在處理線上問題的時(shí)候需要最快速、最便捷的命令,因此arthas idea 插件還是有存在的意義和價(jià)值的。
arthas idea plugin** 這個(gè)插件的意義不是處理協(xié)議層面的問題,主要解決命令生成的問題,由于工程在idea 里面管理,你想想你要watch 哪個(gè)類,這個(gè)插件是知道的,幫助你更方便、更加快捷的構(gòu)建命令。使用arthas idea 插件 這一點(diǎn)一定要理解哦!主要解決你如何構(gòu)造命令的問題! 更多查看文檔 **解決的問題** - spring 環(huán)境變量優(yōu)先級問題 - 獲取靜態(tài)變量 - 火焰圖集成 - logger 命令集成 - 反編譯集成 - trace -E 集成 - tt 集成 ....... 基本上你能夠在arths 上面看到的功能都集成到了這個(gè)上面!直接在idea 里面搜索arths idea 即可安裝。 **常用特殊用法問題
*靜態(tài)變量** 可以直接獲取 ognl 獲取 ``` ognl -x 3 '@com.wangji92.arthas.plugin.demo.controller.StaticTest@INVOKE_STATIC_DOUBLE' -c e374b99 ``` 可以通過watch 獲取 (光標(biāo)放置在字段上) ``` watch com.wangji92.arthas.plugin.demo.controller.StaticTest * '{params,returnObj,throwExp,@com.wangji92.arthas.plugin.demo.controller.StaticTest@INVOKE_STATIC_DOUBLE}' -n 5 -x 3 '1==1' ``` **一般的變量** 可以通過spring context.getBean().field 獲取(這個(gè)是要配置一個(gè)靜態(tài)的spring context 看使用文檔) tt 、watch 也是可以的哦~ 一樣的原理 ``` ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getBean("staticTest").filedValue' -c e374b99 ``` watch 獲取 放置在字段上即可 ``` watch com.wangji92.arthas.plugin.demo.controller.StaticTest * '{params,returnObj,throwExp,target.filedValue}' -n 5 -x 3 'method.initMethod(),method.constructor!=null || !@java.lang.reflect.Modifier@isStatic(method.method.getModifiers())' ``` 選擇的配置項(xiàng)的值 ``` springContext.getEnvironment() (這個(gè)是要配置一個(gè)靜態(tài)的spring context 看使用文檔) ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getEnvironment().getProperty("custom.name")' -c e374b99 ``` 獲取所有的配置項(xiàng)的值 watch 獲取spring context tt 、static 也是可以的哦~ 一樣的原理 ``` watch -x 3 -n 1 org.springframework.web.servlet.DispatcherServlet doDispatch '#springContext=@org.springframework.web.context.support.WebApplicationContextUtils@getWebApplicationContext(params.getServletContext()),#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' ```
聲明:免責(zé)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn)自行上傳,本網(wǎng)站不擁有所有權(quán),也不承認(rèn)相關(guān)法律責(zé)任。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,請發(fā)
送郵件至:operations@xinnet.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),本站將立刻刪除涉嫌侵權(quán)內(nèi)容。本站原創(chuàng)內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)
需注明出處:新網(wǎng)idc知識百科