arthas通過(guò)連接目標(biāo)Java進(jìn)程實(shí)現(xiàn)線上診斷,核心流程為上傳arthas包、啟動(dòng)并選擇進(jìn)程pid連接、執(zhí)行命令分析結(jié)果。1. 上傳arthas-boot.jar至服務(wù)器;2. 執(zhí)行java -jar arthas-boot.jar列出java進(jìn)程;3. 輸入目標(biāo)pid完成attach;4. 使用dashboard、Thread、trace、watch等命令排查問(wèn)題;5. 注意權(quán)限、性能開(kāi)銷、誤操作風(fēng)險(xiǎn)及版本兼容性等問(wèn)題。
Arthas,這個(gè)名字在Java線上診斷領(lǐng)域,簡(jiǎn)直就是“救世主”的代名詞。它能讓你在不重啟應(yīng)用的情況下,深入jvm內(nèi)部,洞察一切運(yùn)行細(xì)節(jié),無(wú)論是CPU飆高、線程死鎖,還是方法調(diào)用鏈路的性能瓶頸,Arthas都能幫你抽絲剝繭,找到癥結(jié)所在。對(duì)于那些被線上偶發(fā)問(wèn)題折磨得焦頭爛額的開(kāi)發(fā)者來(lái)說(shuō),掌握Arthas,就像擁有了一把趁手的“手術(shù)刀”。
解決方案
要用Arthas進(jìn)行線上診斷,核心流程其實(shí)就那么幾步:連接目標(biāo)Java進(jìn)程、執(zhí)行診斷命令、分析輸出結(jié)果。首先,你得確保目標(biāo)機(jī)器上已經(jīng)部署了Arthas的發(fā)行包。通常,我們會(huì)把a(bǔ)rthas-boot.jar或者整個(gè)arthas-packaging目錄上傳到服務(wù)器上。然后,通過(guò)java -jar arthas-boot.jar命令啟動(dòng),它會(huì)自動(dòng)列出當(dāng)前機(jī)器上運(yùn)行的Java進(jìn)程,你選擇一個(gè)PID就能attach上去。一旦連接成功,一個(gè)交互式的命令行界面就呈現(xiàn)在你面前了,各種診斷命令任你施展。
Arthas如何連接到運(yùn)行中的Java應(yīng)用?
連接Arthas到目標(biāo)Java應(yīng)用,這事兒看似簡(jiǎn)單,但有時(shí)也挺磨人。最常見(jiàn)的方式,也是我個(gè)人最推薦的,就是用arthas-boot.jar。你把它上傳到服務(wù)器上,然后執(zhí)行java -jar arthas-boot.jar。它會(huì)自動(dòng)掃描并列出當(dāng)前服務(wù)器上所有的Java進(jìn)程及其對(duì)應(yīng)的PID。你只需要輸入你想連接的那個(gè)進(jìn)程的PID,回車,Arthas就嘗試attach了。
立即學(xué)習(xí)“Java免費(fèi)學(xué)習(xí)筆記(深入)”;
當(dāng)然,也有一些小“坑”你可能會(huì)遇到。比如,如果你的Java應(yīng)用是以特定用戶啟動(dòng)的,而你用另一個(gè)用戶去運(yùn)行arthas-boot.jar,可能會(huì)遇到權(quán)限問(wèn)題,導(dǎo)致無(wú)法attach。這時(shí)候,確保Arthas運(yùn)行用戶和目標(biāo)Java應(yīng)用的用戶一致,或者至少有足夠的權(quán)限去訪問(wèn)目標(biāo)進(jìn)程。還有,JAVA_HOME環(huán)境變量的設(shè)置也很關(guān)鍵,Arthas需要知道Java運(yùn)行時(shí)環(huán)境在哪。我記得有一次,就是因?yàn)榉?wù)器上裝了多個(gè)JDK版本,默認(rèn)的JAVA_HOME指向了一個(gè)Arthas不支持的版本,折騰了好久才發(fā)現(xiàn)。
另外,如果你想讓Arthas作為Java agent隨應(yīng)用啟動(dòng),也可以在啟動(dòng)參數(shù)里加上-javaagent:/path/to/arthas-agent.jar,但這通常用于更高級(jí)的場(chǎng)景,比如應(yīng)用啟動(dòng)初期就想介入診斷,或者在容器化環(huán)境中更方便地集成。不過(guò),對(duì)于日常的線上問(wèn)題排查,動(dòng)態(tài)attach已經(jīng)足夠強(qiáng)大了。
線上診斷時(shí),Arthas有哪些核心命令能幫上忙?
Arthas的命令集簡(jiǎn)直是寶藏,每一個(gè)都可能在關(guān)鍵時(shí)刻幫你大忙。我通常會(huì)根據(jù)問(wèn)題類型來(lái)選擇:
- CPU飆高? dashboard和thread是首選。dashboard能給你一個(gè)全局概覽,包括CPU、內(nèi)存、GC等信息。如果CPU異常,我會(huì)立刻轉(zhuǎn)到thread -n 3(查看CPU占用最高的3個(gè)線程),或者thread -i 1000(每秒打印一次線程棧,看哪個(gè)線程一直在跑),迅速定位到是哪個(gè)線程出了問(wèn)題。拿到線程ID后,thread
就能看到詳細(xì)的堆棧信息,基本就能鎖定是哪段代碼或者哪個(gè)業(yè)務(wù)邏輯導(dǎo)致了CPU高負(fù)載。 - 應(yīng)用卡頓,但CPU不高? 這時(shí)候可能就是死鎖或者等待資源。thread -b能幫你找出所有可能存在的死鎖。如果不是死鎖,thread命令的輸出里,那些狀態(tài)為WaiTING、BLOCKED的線程,它們的堆棧信息往往能揭示它們?cè)诘却裁促Y源,比如數(shù)據(jù)庫(kù)連接、鎖、網(wǎng)絡(luò)IO等。
- 方法耗時(shí)異常? trace和watch是利器。trace com.example.MyService myMethod可以追蹤myMethod的調(diào)用路徑和每個(gè)子方法的耗時(shí),幫你找出耗時(shí)的具體環(huán)節(jié)。如果只想看方法入?yún)⒑头祷刂?,watch com.example.MyService myMethod ‘{params, returnObj}’ -x 2就非常方便,它能在方法執(zhí)行前后打印參數(shù)和返回值,而且x參數(shù)還能控制展開(kāi)深度,避免輸出一大堆不關(guān)心的內(nèi)部細(xì)節(jié)。
- 想看類加載情況或反編譯代碼? sc(search class)和sm(search method)能幫你快速定位到內(nèi)存中的類和方法。然后jad com.example.MyClass直接就能把這個(gè)類反編譯出來(lái),看看線上運(yùn)行的代碼是不是你預(yù)期的版本,或者有沒(méi)有被某些框架動(dòng)態(tài)增強(qiáng)過(guò)。這在排查類加載沖突或者某些詭異行為時(shí),簡(jiǎn)直是神器。
- 想動(dòng)態(tài)修改變量值或者熱更新代碼? ognl和redefine。ognl能讓你執(zhí)行任意的OGNL表達(dá)式,直接操作內(nèi)存中的對(duì)象,比如修改一個(gè)靜態(tài)配置變量。redefine更是強(qiáng)大,它能讓你在不重啟應(yīng)用的情況下,重新加載修改過(guò)的class文件。我個(gè)人對(duì)redefine持謹(jǐn)慎態(tài)度,因?yàn)橐坏┎僮鞑划?dāng),可能會(huì)引入新的問(wèn)題甚至導(dǎo)致應(yīng)用崩潰,但它在某些緊急場(chǎng)景下確實(shí)是救命稻草。
使用Arthas進(jìn)行線上問(wèn)題排查時(shí),有哪些常見(jiàn)的“坑”和注意事項(xiàng)?
使用Arthas雖然強(qiáng)大,但也得小心,畢竟是在生產(chǎn)環(huán)境直接操作。我踩過(guò)不少坑,也總結(jié)了一些經(jīng)驗(yàn):
- 性能開(kāi)銷: trace和watch命令雖然好用,但如果你的目標(biāo)方法調(diào)用非常頻繁,或者你設(shè)置的條件過(guò)濾過(guò)于寬泛,它們可能會(huì)帶來(lái)不小的性能開(kāi)銷,甚至拖垮應(yīng)用。所以,在使用這些命令時(shí),一定要加上#cost(限制耗時(shí))或者condition(條件過(guò)濾),比如trace com.example.MyService myMethod ‘#cost > 100’,只追蹤耗時(shí)超過(guò)100毫秒的調(diào)用。
- 安全與權(quán)限: 生產(chǎn)環(huán)境的權(quán)限控制非常重要。Arthas能做的事情太多了,幾乎可以完全控制JVM。因此,Arthas的部署和使用權(quán)限必須嚴(yán)格管理,避免未經(jīng)授權(quán)的人員進(jìn)行危險(xiǎn)操作。我通常會(huì)建議只在需要時(shí)才上傳和啟動(dòng)Arthas,用完立即清理。
- 日志輸出過(guò)量: 某些命令,比如stack,如果目標(biāo)方法調(diào)用頻繁,輸出會(huì)非常多,瞬間刷屏。這時(shí)候,你可以結(jié)合grep或者less命令來(lái)過(guò)濾和分頁(yè)查看。Arthas本身也支持> filename將輸出重定向到文件。
- 連接問(wèn)題: 除了前面提到的權(quán)限和JDK版本,網(wǎng)絡(luò)隔離也可能導(dǎo)致Arthas無(wú)法連接。如果你的應(yīng)用運(yùn)行在容器內(nèi)部或者有嚴(yán)格的網(wǎng)絡(luò)策略,可能需要開(kāi)放相應(yīng)的端口(Arthas默認(rèn)會(huì)使用一些端口進(jìn)行通信,盡管attach模式下通常是IPC),或者通過(guò)宿主機(jī)進(jìn)行端口映射。
- 誤操作風(fēng)險(xiǎn): redefine、ognl這些命令,威力巨大,也伴隨著風(fēng)險(xiǎn)。尤其是在生產(chǎn)環(huán)境,任何不當(dāng)?shù)牟僮鞫伎赡軐?dǎo)致應(yīng)用狀態(tài)異常,甚至崩潰。在不確定的時(shí)候,寧愿多花點(diǎn)時(shí)間分析,也不要輕易嘗試這些高風(fēng)險(xiǎn)操作。我個(gè)人習(xí)慣在執(zhí)行這類命令前,再三確認(rèn)目標(biāo)和影響范圍。
- 版本兼容性: Arthas自身也在不斷迭代,有時(shí)新版本可能對(duì)舊的JVM版本支持不好,或者某些命令行為有變化。所以,在生產(chǎn)環(huán)境使用前,最好在測(cè)試環(huán)境驗(yàn)證一下Arthas的版本和目標(biāo)JDK版本的兼容性。
總的來(lái)說(shuō),Arthas是個(gè)雙刃劍,用得好,事半功倍;用不好,可能適得其反。關(guān)鍵在于理解其原理,掌握常用命令,并始終保持一顆敬畏之心。