arthas學(xué)習(xí)

啟動arthas:
wget https://alibaba.github.io/arthas/arthas-boot.jar
使用aliyun的鏡像:java -jar arthas-boot.jar --repo-mirror aliyun --use-http

  java -jar arthas-boot.jar <pid>

  sudo -u tomcat java -jar arthas-boot.jar

手動可參考 https://alibaba.github.io/arthas/manual-install.html

1:獲取安裝包
wget https://maven.aliyun.com/repository/public/com/taobao/arthas/arthas-packaging/3.0.4/arthas-packaging-3.0.4-bin.zip
2:解壓安裝包
 unzip arthas-packaging-3.0.4-bin.zip
3:安裝
 ./install-local.sh
4:運行
 ./as.sh
或者 sudo -u tomcat -EH ./as.sh

遇到的坑:

  • 下載腳本不要放在家目錄,用戶之前無法互相訪問家目錄。腳本放在自己家目錄 tomcat無法訪問你的家目錄,會執(zhí)行出錯
  • 執(zhí)行用戶需要和java用戶保持身份一致。

如果找不到進程,可以如下方式:
curl -L https://alibaba.github.io/arthas/install.sh | sh

命令:
查看dashboard

通過thread命令來獲取到arthas-demo進程的Main Class
thread 1會打印線程ID 1的棧,通常是main函數(shù)的線程。

$ thread 1 | grep 'main('
    at demo.MathGame.main(MathGame.java:17)

通過jad來反編繹Main Class
$ jad demo.MathGame

watch命令

通過watch命令來查看demo.MathGame#primeFactors函數(shù)的返回值

$ watch demo.MathGame primeFactors returnObj
觀察方法出參和返回值
$ watch demo.MathGame primeFactors "{params,returnObj}" -x 2
觀察方法入?yún)?$ watch demo.MathGame primeFactors "{params,returnObj}" -x 2 -b
條件限制    - #只有滿足條件的調(diào)用,才會有響應(yīng)
$ watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0"
觀察異常信息的例子
$ watch demo.MathGame primeFactors "{params[0],throwExp}" -e -x 2
按照耗時進行過濾      - #cost>200(單位是ms)
#watch/stack/trace這個三個命令都支持#cost
$ watch demo.MathGame primeFactors '{params, returnObj}' '#cost>200' -x 2
觀察當(dāng)前對象中的屬性
$ watch demo.MathGame primeFactors 'target'
使用target.field_name訪問當(dāng)前對象的某個屬性
$ watch demo.MathGame primeFactors 'target.illegalArgumentCount'
觀察類中所有拋出異常的方法信息
$ watch com.example.UserService * -e -x 2 '{params,throwExp}'

同時觀察方法調(diào)用前和方法返回后
#參數(shù)里-n 2,表示只執(zhí)行兩次
#結(jié)果的輸出順序和事件發(fā)生的先后順序一致,和命令中 -s -b 的順序無關(guān)
#-x表示遍歷深度,可以調(diào)整來打印具體的參數(shù)和結(jié)果內(nèi)容,默認值是1
$ watch demo.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s -n 2

特別說明

  • watch 命令定義了4個觀察事件點,即 -b 方法調(diào)用前,-e 方法異常后,-s 方法返回后,-f 方法結(jié)束后
  • 4個觀察事件點 -b、-e、-s 默認關(guān)閉,-f 默認打開,當(dāng)指定觀察點被打開后,在相應(yīng)事件點會對觀察表達式進行求值并輸出
  • 這里要注意方法入?yún)⒑头椒ǔ鰠⒌膮^(qū)別,有可能在中間被修改導(dǎo)致前后不一致,除了 -b 事件點 params 代表方法入?yún)⑼猓溆嗍录即矸椒ǔ鰠?/li>
  • 當(dāng)使用 -b 時,由于觀察事件點是在方法調(diào)用前,此時返回值或異常均不存在

觀察表達式的構(gòu)成主要由 ognl 表達式組成,所以你可以這樣寫"{params,returnObj}",只要是一個合法的 ognl 表達式,都能被正常支持。觀察的維度也比較多,主要體現(xiàn)在參數(shù) advice 的數(shù)據(jù)結(jié)構(gòu)上。Advice 參數(shù)最主要是封裝了通知節(jié)點的所有信息。
用法:https://alibaba.github.io/arthas/advice-class.html


如果只是退出當(dāng)前的連接,可以用quit或者exit命令。Attach到目標進程上的arthas還會繼續(xù)運行,端口會保持開放,下次連接時可以直接連接上。

如果想完全退出arthas,可以執(zhí)行shutdown命令。

jvm相關(guān)

  • dashboard——當(dāng)前系統(tǒng)的實時數(shù)據(jù)面板
  • thread——查看當(dāng)前 JVM 的線程堆棧信息
  • jvm——查看當(dāng)前 JVM 的信息
  • sysprop——查看和修改JVM的系統(tǒng)屬性
  • sysenv——查看JVM的環(huán)境變量
  • getstatic——查看類的靜態(tài)屬性
  • New! ognl——執(zhí)行ognl表達式

mc 編譯指令

Memory Compiler/內(nèi)存編繹器,編繹.java文件生成.class。
mc /tmp/Test.java
可以通過-c參數(shù)指定classloader:
mc -c 327a647b /tmp/Test.java
可以通過-d命令指定輸出目錄:
mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java

編繹生成.class文件之后,可以結(jié)合redefine命令實現(xiàn)熱更新代碼

動態(tài)增加日志 redefine

在Arthas里,可以通過redefine命令來達到線上不重啟,動態(tài)更新代碼的效果
添加日志,本地編繹后,把.class文件傳到線上服務(wù)器,然后用redefine命令來更新代碼

$ redefine -p /tmp/UserServiceImpl.class
redefine success, size: 1

三個命令結(jié)合使用 用過vim等編輯后最終redefine

jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
 
mc /tmp/UserController.java -d /tmp
 
redefine /tmp/com/example/demo/arthas/user/UserController.class

注意:

  • 正在跑的函數(shù),沒有退出不能生效 比如while(true)循環(huán)
  • 不允許新增加field/method

記錄調(diào)用

$ tt -t com.example.webtest.controller.testController testResult
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 12 ms.
 INDEX     TIMESTAMP              COST(ms)    IS-RET    IS-EXP   OBJECT            CLASS                               METHOD
----------------------------------------------------------------------------------------------------------------------------------------------------------
 1002      2019-02-12 16:14:25    0.367999    true      false    0x6ad213f7        testController                      testResult

命令參數(shù)解析
-t
tt 命令有很多個主參數(shù),-t 就是其中之一。這個參數(shù)的表明希望記錄下類 *Test 的 print 方法的每次執(zhí)行情況。

-n 3
當(dāng)你執(zhí)行一個調(diào)用量不高的方法時可能你還能有足夠的時間用 CTRL+C 中斷 tt 命令記錄的過程,但如果遇到調(diào)用量非常大的方法,瞬間就能將你的 JVM 內(nèi)存撐爆。
此時你可以通過 -n 參數(shù)指定你需要記錄的次數(shù),當(dāng)達到記錄次數(shù)時 Arthas 會主動中斷tt命令的記錄過程,避免人工操作無法停止的情況。

解決方法重載
tt -t *Test print params[0].length==1
通過制定參數(shù)個數(shù)的形式解決不同的方法簽名,如果參數(shù)個數(shù)一樣,你還可以這樣寫
tt -t *Test print 'params[1] instanceof Integer'
解決指定參數(shù)
tt -t *Test print params[0].mobile=="13989838402"
查看檢索到的記錄
tt -l
查看調(diào)用具體信息
tt -i 1003
對于具體一個時間片的信息而言,你可以通過 -i 參數(shù)后邊跟著對應(yīng)的 INDEX 編號查看到他的詳細信息
回放調(diào)用
tt -i 1004 -p

$ tt -i 1004 -p
 RE-INDEX       1004
 GMT-REPLAY     2018-12-04 11:26:00
 OBJECT         0x4b67cf4d
 CLASS          demo.MathGame
 METHOD         primeFactors
 PARAMETERS[0]  @Integer[946738738]
 IS-RETURN      true
 IS-EXCEPTION   false
 RETURN-OBJ     @ArrayList[
                    @Integer[2],
                    @Integer[11],
                    @Integer[17],
                    @Integer[2531387],
                ]
Time fragment[1004] successfully replayed.
Affect(row-cnt:1) cost in 14 ms.

tt 命令由于保存了當(dāng)時調(diào)用的所有現(xiàn)場信息,所以我們可以自己主動對一個 INDEX 編號的時間片自主發(fā)起一次調(diào)用,從而解放你的溝通成本。此時你需要 -p 參數(shù)。

需要強調(diào)的點

  • ThreadLocal 信息丟失
    很多框架偷偷的將一些環(huán)境變量信息塞到了發(fā)起調(diào)用線程的 ThreadLocal 中,由于調(diào)用線程發(fā)生了變化,這些 ThreadLocal 線程信息無法通過 Arthas 保存,所以這些信息將會丟失。
    一些常見的 CASE 比如:鷹眼的 TraceId 等。
  • 引用的對象
    需要強調(diào)的是,tt 命令是將當(dāng)前環(huán)境的對象引用保存起來,但僅僅也只能保存一個引用而已。如果方法內(nèi)部對入?yún)⑦M行了變更,或者返回的對象經(jīng)過了后續(xù)的處理,那么在 tt 查看的時候?qū)o法看到當(dāng)時最準確的值。這也是為什么 watch 命令存在的意義。

獲取Spring context

除了上面介紹的一些排查技巧,下面分享一個獲取Spring Context,然后“為所欲為”的例子。
在Dubbo里有一個擴展com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory,把Spring Context保存到了里面。
因此,我們可以通過ognl命令獲取到。

$ ognl '#context=@com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory@contexts.iterator.next, #context.getBean("userServiceImpl").findUser(1)'
@User[
    id=@Integer[1],
    name=@String[Deanna Borer],
]
SpringExtensionFactory@contexts.iterator.next 獲取到SpringExtensionFactory里保存的spring context對象
#context.getBean("userServiceImpl").findUser(1) 獲取到userServiceImpl再執(zhí)行一次調(diào)用

Dubbo運行時有哪些Filter? 耗時是多少?

trace函數(shù)
-j 過濾掉jdk的函數(shù)

$ trace -j  demo.MathGame run
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 31 ms.
`---ts=2018-12-04 01:09:14;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[5.190646ms] demo.MathGame:run()
        +---[4.465779ms] demo.MathGame:primeFactors()
        `---[0.375324ms] demo.MathGame:print()

據(jù)調(diào)用耗時過濾
trace demo.MathGame run '#cost > 10'

$ trace com.alibaba.dubbo.rpc.Filter *
Press Ctrl+C to abort.
Affect(class-cnt:19 , method-cnt:59) cost in 1441 ms.
`---ts=2018-12-05 19:07:26;thread_name=DubboServerHandler-30.5.125.152:20880-thread-10;id=3e;is_daemon=true;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@5c647e05
    `---[8.435844ms] com.alibaba.dubbo.rpc.filter.EchoFilter:invoke()
        +---[0.124572ms] com.alibaba.dubbo.rpc.Invocation:getMethodName()
        +---[0.065123ms] java.lang.String:equals()
        `---[7.762928ms] com.alibaba.dubbo.rpc.Invoker:invoke()
            `---[7.494124ms] com.alibaba.dubbo.rpc.filter.ClassLoaderFilter:invoke()
                +---[min=0.00355ms,max=0.049922ms,total=0.057637ms,count=3] java.lang.Thread:currentThread()
                +---[0.0126ms] java.lang.Thread:getContextClassLoader()
                +---[0.02188ms] com.alibaba.dubbo.rpc.Invoker:getInterface()
                +---[0.004115ms] java.lang.Class:getClassLoader()
                +---[min=0.003906ms,max=0.014058ms,total=0.017964ms,count=2] java.lang.Thread:setContextClassLoader()
                `---[7.033486ms] com.alibaba.dubbo.rpc.Invoker:invoke()
                    `---[6.869488ms] com.alibaba.dubbo.rpc.filter.GenericFilter:invoke()
                        +---[0.01481ms] com.alibaba.dubbo.rpc.Invocation:getMethodName()


動態(tài)修改Dubbo的logger級別

在排查問題時,需要查看到更多的信息,如果可以把logger級別修改為DEBUG,就非常有幫助。
ognl是apache開源的一個輕量級表達式引擎。下面通過Arthas里的ognl命令來動態(tài)修改logger級別。
首先獲取Dubbo里TraceFilter的一個logger對象,看下它的實現(xiàn)類,可以發(fā)現(xiàn)是log4j

$ ognl '@com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter@logger.logger'
@Log4jLogger[
    FQCN=@String[com.alibaba.dubbo.common.logger.support.FailsafeLogger],
    logger=@Logger[org.apache.log4j.Logger@2f19bdcf],
]

再用sc命令來查看具體從哪個jar包里加載的

$ sc -d org.apache.log4j.Logger
 class-info        org.apache.log4j.Logger
 code-source       /Users/hengyunabc/.m2/repository/org/slf4j/log4j-over-slf4j/1.7.25/log4j-over-slf4j-1.7.25.jar
 name              org.apache.log4j.Logger
 isInterface       false
 isAnnotation      false
 isEnum            false
 isAnonymousClass  false
 isArray           false
 isLocalClass      false
 isMemberClass     false
 isPrimitive       false
 isSynthetic       false
 simple-name       Logger
 modifier          public
 annotation
 interfaces
 super-class       +-org.apache.log4j.Category
                     +-java.lang.Object
 class-loader      +-sun.misc.Launcher$AppClassLoader@5c647e05
                     +-sun.misc.Launcher$ExtClassLoader@59878d35
 classLoaderHash   5c647e05

Affect(row-cnt:1) cost in 126 ms.

可以看到log4j是通過slf4j代理的。
那么通過org.slf4j.LoggerFactory獲取root logger,再修改它的level:

$ ognl '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'
null
$ ognl '@org.slf4j.LoggerFactory@getLogger("root").getLevel().toString()'
@String[DEBUG]

sc命令

“Search-Class” 的簡寫,這個命令能搜索出所有已經(jīng)加載到 JVM 中的 Class 信息

打印類的詳細信息
sc -d com.example.webtest.controller.testController
打印出類的Field信息
sc -d -f demo.MathGame

ognl

https://commons.apache.org/proper/commons-ognl/language-guide.html

$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
@ArrayList[
    @String[/opt/java/8.0.181-zulu/jre],
    @String[OpenJDK Runtime Environment],
]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容