轉(zhuǎn)載本文需注明出處:微信公眾號(hào)EAWorld,違者必究。
什么是Arthas
Java 診斷利器 Arthas,是阿里的一款開(kāi)源工具。Github-alibaba/arthas 上可以看到它的介紹。
在日常開(kāi)發(fā)中,當(dāng)我們發(fā)現(xiàn)應(yīng)用的某個(gè)接口響應(yīng)比較慢,這個(gè)時(shí)候想想要分析一下原因,找到代碼中耗時(shí)的部分,比較容易想到的是在接口鏈路的 IO 操作上下游打印時(shí)間日志,再根據(jù)幾個(gè)時(shí)間點(diǎn)的日志算出耗時(shí)長(zhǎng)的 IO 操作。這種方式?jīng)]有問(wèn)題,但是加日志需要發(fā)布,既繁瑣又低效,這個(gè)時(shí)候可以引入一些線(xiàn)上 debug 的工具,arthas 就是很好的一種,除了分析耗時(shí),還可以打印調(diào)用棧、方法入?yún)⒓胺祷兀?lèi)加載情況,線(xiàn)程池狀態(tài),系統(tǒng)參數(shù)等等,其實(shí)現(xiàn)原理是解析 JVM 在操作系統(tǒng)中的文件,大部分操作是只讀的,對(duì)服務(wù)進(jìn)程沒(méi)有侵入性,因此可以放心使用。
Arthas能解決什么問(wèn)題?
從我個(gè)人的使用經(jīng)驗(yàn)來(lái)看,Arthas至少幫我解決了以下幾個(gè)問(wèn)題:
監(jiān)控某方法查看其整個(gè)調(diào)用鏈路,從而找出某次調(diào)用的性能瓶頸。
反編譯某個(gè)類(lèi),查看線(xiàn)上的代碼是否與本地代碼一致,避免沒(méi)有提交代碼而導(dǎo)致的問(wèn)題。
站在全局視角查看整個(gè)系統(tǒng)的運(yùn)行情況,比如觀(guān)察內(nèi)存增長(zhǎng)、GC的情況。
在微服務(wù)架構(gòu)背景下,通常本地沒(méi)有完整的環(huán)境,此時(shí)可以直接在測(cè)試環(huán)境監(jiān)控某個(gè)方法傳入的參數(shù)是否正確,返回值是否正確。
快速開(kāi)始
下載并啟動(dòng)arthas
輸入以下命令啟動(dòng)arthas程序
curl -O https://arthas.a(chǎn)liyun.com/arthas-demo.jarjava -jar arthas-demo.jar
選擇需要監(jiān)控應(yīng)用的進(jìn)程編號(hào),回車(chē)后Arthas會(huì)attach到目標(biāo)進(jìn)程上,并輸出日志:
容器中使用
docker exec -it arthas-demo /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar"
甚至我們可以直接把a(bǔ)rthas放到容器鏡像文件中:
COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas
Arthas常用命令
下面簡(jiǎn)要為大家介紹一下Arthas常用的幾個(gè)命令。
dashboard
使用dashboard命令會(huì)顯示以下信息:
1. 當(dāng)前最繁忙的一些線(xiàn)程
2. 顯示應(yīng)用當(dāng)前使用的內(nèi)存信息,包括堆內(nèi)存、新生代伊甸區(qū)、老年代、非堆內(nèi)存的使用情況。
3. 顯示程序的GC情況,包括YGC次數(shù)、總花費(fèi)時(shí)間,F(xiàn)GC的次數(shù)、總花費(fèi)時(shí)間。
4. 當(dāng)前程序的一些信息,包括系統(tǒng)版本、JDK版本、程序運(yùn)行時(shí)長(zhǎng)等。
Thread
查看當(dāng)前線(xiàn)程信息,查看線(xiàn)程的堆棧
// 查看 CPU 使用率 top n 的線(xiàn)程的棧,當(dāng)前最忙的前 n 個(gè)線(xiàn)程:thread -n 3// 查看 5 秒內(nèi)的 CPU 使用率 top n 的線(xiàn)程棧:thread -n 3 -i 5000
// 查看線(xiàn)程是否有阻塞:thread -b
watch
有時(shí)候我們不太方便進(jìn)行本地調(diào)試,這個(gè)時(shí)候可以使用watch監(jiān)控某個(gè)方法,然后點(diǎn)擊相關(guān)的功能,查看該方法的調(diào)用情況:
watch com.primeton.dgp.a(chǎn)ssets.controller.DsResourcePoolController getResourcePool "{params,returnObj}" -x 2
執(zhí)行以上命令后,控制臺(tái)會(huì)hold在被監(jiān)控的方法上,此時(shí)再次點(diǎn)擊頁(yè)面上的按鈕,控制臺(tái)就會(huì)打印此次的調(diào)用結(jié)果。
trace
trace 命令能主動(dòng)搜索 class-pattern/method-pattern 對(duì)應(yīng)的方法調(diào)用路徑,渲染和統(tǒng)計(jì)整個(gè)調(diào)用鏈路上的所有性能開(kāi)銷(xiāo)和追蹤調(diào)用鏈路。通常我們用這個(gè)方法可以看到整個(gè)調(diào)用路徑的耗時(shí),從而找出真正的性能瓶頸,幫助解決問(wèn)題。
trace com.primeton.dgp.a(chǎn)ssets.controller.DsResourcePoolController getResourcePool
tt
tt方法執(zhí)行數(shù)據(jù)的時(shí)空隧道,記錄下指定方法每次調(diào)用的入?yún)⒑头祷匦畔?,并能?duì)這些不同的時(shí)間下調(diào)用進(jìn)行觀(guān)測(cè),使用方法如下:
tt -t com.primeton.dgp.a(chǎn)ssets.controller.DsResourcePoolController getResourcePool
執(zhí)行此命令后,arthas會(huì)記錄下指定方法的每次調(diào)用環(huán)境現(xiàn)場(chǎng),并打印一些相關(guān)的信息,如調(diào)用時(shí)長(zhǎng)、是否正常返回、是否拋出異常等。
顯示所有被記錄的調(diào)用列表
tt -l
重做一次調(diào)用。tt 命令由于保存了當(dāng)時(shí)調(diào)用的所有現(xiàn)場(chǎng)信息,所以我們可以自己主動(dòng)對(duì)一個(gè) INDEX 編號(hào)的時(shí)間片自主發(fā)起一次調(diào)用,從而解放你的溝通成本。此時(shí)你需要 -p 參數(shù)。通過(guò) --replay-times 指定 調(diào)用次數(shù),通過(guò) --replay-interval 指定多次調(diào)用間隔(單位ms, 默認(rèn)1000ms)
tt -i 1001 -p --replay-times 3 --replay-interval 3000
jad
jad 命令將 JVM 中實(shí)際運(yùn)行的 class 的 byte code 反編譯成 java 代碼,便于你理解業(yè)務(wù)邏輯;反編譯出來(lái)的源碼是帶語(yǔ)法高亮的,閱讀更方便。當(dāng)我們看到某個(gè)方法的調(diào)用時(shí)長(zhǎng)明顯過(guò)長(zhǎng),或者時(shí)空隧道中某方法拋出異常,這個(gè)時(shí)候就可以直接反編譯相關(guān)的方法,看看是否能找出原因。
其他
除了以上很常用的命令以外,arthas還有很多實(shí)用命令,大家可以參考官方文檔來(lái)使用
另外,每一個(gè)命令都支持-h(huán)elp參數(shù),可以查看該命令的說(shuō)明、參數(shù),甚至包括了使用例子,可以說(shuō)是非常貼心了
案例
案例1
我們準(zhǔn)備了一個(gè)簡(jiǎn)單的springboot應(yīng)用,應(yīng)用內(nèi)有一個(gè)Controller,返回一個(gè)字符串,代碼如下:
@RestControllerpublic class DemoController {
@GetMapping(value = "/getDemo")public String getDemo(){return "demo 1"; }}
啟動(dòng)程序,訪(fǎng)問(wèn)剛剛準(zhǔn)備好的controller得到了下面的結(jié)果
接下來(lái)我們要嘗試在沒(méi)有源碼的情況下修改demo controller的返回值,并在不重啟應(yīng)用的情況下,使我們修改的返回值生效。
首先啟動(dòng)arthas并織入到剛才的demo,然后反編譯DemoController到臨時(shí)文件夾。
jad --source-only com.example.a(chǎn)rthas.DemoController > /tmp/DemoController.java
接下來(lái)我們用vim修改反編譯出來(lái)的代碼
查詢(xún)加載原有DemoController類(lèi)的ClassLoader
sc -d com.example.a(chǎn)rthas.DemoController
重新編譯修改后的類(lèi)
mc -c 685f4c2e /tmp/DemoController.java
重新加載修改后的類(lèi)
redefine -c 685f4c2e /opt/arthas/demo/com/example/arthas/DemoController.class
此時(shí),無(wú)需重啟應(yīng)用,我們重新訪(fǎng)問(wèn)剛才的controller,會(huì)發(fā)現(xiàn)返回結(jié)果已經(jīng)成功更改了。
案例2
某燃?xì)庀到y(tǒng)由于被眾多地區(qū)的燃?xì)?a target="_blank">公司所使用,所以版本繁多,經(jīng)歷過(guò)很多輪的測(cè)試,其中比較耗時(shí)的當(dāng)屬性能測(cè)試了。以往性能測(cè)試工作通常耗時(shí)很長(zhǎng),主要是性能測(cè)試人員并不一定熟悉項(xiàng)目代碼,導(dǎo)致壓力測(cè)試出現(xiàn)問(wèn)題的時(shí)候不容易找到問(wèn)題的所在。
在本次的表具接口的性能測(cè)試中,我們使用Arthas工具進(jìn)行故障的快速定位,大幅減少了性能測(cè)試的時(shí)間,達(dá)到了很好的效果。
本次壓力測(cè)試的過(guò)程中我們發(fā)現(xiàn),當(dāng)并發(fā)達(dá)到一定數(shù)量的時(shí)候系統(tǒng)會(huì)卡死,大量新的請(qǐng)求會(huì)超時(shí)。使用dashboard查看發(fā)現(xiàn)系統(tǒng)的大量線(xiàn)程處于block或者wating中。
使用trace命令,將壓力測(cè)試的方法進(jìn)行監(jiān)控,并保存到日志文件中,接著重新跑一輪壓力測(cè)試。
trace -n 150000 com.towngas.tcis.interfaces.nb.NbInfoServlet getService >> tt-2020-10-17-NbServlet-getService
監(jiān)控日志,當(dāng)用戶(hù)數(shù)達(dá)到500+的時(shí)候果然發(fā)現(xiàn)了問(wèn)題
繼續(xù)使用trace命令跟蹤,發(fā)現(xiàn)了可能出現(xiàn)問(wèn)題的方法
使用jad命令反編譯該代碼可以看到HttpClient并沒(méi)有被立即關(guān)閉,使用release的釋放后關(guān)閉通常沒(méi)有問(wèn)題,但是釋放資源有一個(gè)過(guò)程通常是180秒,所以如果短時(shí)間有大量接口調(diào)用,會(huì)導(dǎo)致打開(kāi)的socket連接數(shù)量超過(guò)系統(tǒng)設(shè)定值。找到原因后問(wèn)題得以順利解決,壓力測(cè)試得以順利完成。
小結(jié)
本次的分享為大家介紹了Arthas的基本概念與基礎(chǔ)的使用方法。更為詳細(xì)的使用方法、命令的更多參數(shù),還請(qǐng)查看Arthas的官方文檔。
關(guān)于作者:李云濤,普元高級(jí)開(kāi)發(fā)工程師,擅長(zhǎng)性能調(diào)優(yōu)、微服務(wù)、容器、消息隊(duì)列等技術(shù)。先后參與郵儲(chǔ)銀行Java開(kāi)發(fā)平臺(tái)、中移總ERP流程平臺(tái)、中煤信息技術(shù)中臺(tái)等平臺(tái)的的架構(gòu)設(shè)計(jì)與平臺(tái)研發(fā)工作。
-
代碼
+關(guān)注
關(guān)注
30文章
4705瀏覽量
68127 -
編譯
+關(guān)注
關(guān)注
0文章
647瀏覽量
32747
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論