1. 程式人生 > >得虧了它,我才把潛藏那麼深的Bug挖出來

得虧了它,我才把潛藏那麼深的Bug挖出來

2020年寫了很多事故解決的文章,並不是我絞盡腦汁想出來的,而是真的遇到了這些問題。通過文章的方式記錄下來,分享出去,才有意義。 # 事故背景 首先看下面的圖吧,這是我從cat上截的圖。 ![](https://img2020.cnblogs.com/blog/1618095/202003/1618095-20200303100023199-585632569.png) 可以看到是一個Rpc呼叫的錯誤,從錯誤中我們只能分析出這個Rpc的請求成功了,並且返回了,因為都走到了反序列化這步。 最後是在建立DTO物件的時候報錯了,**Could not initalize class xxxxx.DTO**說明了這一點。 作為一個呼叫方,雖然看到了明確的錯誤,但還是要本著嚴謹的態度去排查問題,還是先確認服務提供者到底有沒有問題,跟同事確認了,服務提供方沒問題,通過telnet可以正常invoke。 好了,到這為止就把背景交代清楚了,能不能將這個潛藏的Bug找出來就各顯身手吧。 # arthas大顯身手 要想效率高,那必須得有好用的工具呀!arthas挺身而出,都毛遂自薦了,不用白不用。 首先使用sc命令檢視JVM已載入的類資訊,就看這個不能實列化的類到底有沒有被成功載入。 **sc -d 類全路徑 (****列印類的詳細資訊****)** ![](https://img2020.cnblogs.com/blog/1618095/202003/1618095-20200303100037330-1385181344.png) 類的資訊都被打印出來了,足以證明這個類被載入了。 然後列印下類裡面的欄位,看看有沒有丟失什麼的 **sc -d -f 類全路徑 (****列印****出****類的****Field****資訊****)** ![](https://img2020.cnblogs.com/blog/1618095/202003/1618095-20200303100052988-1233316715.png) 居然報錯了,錯誤還跟我們之前在cat中看到的一模一樣,這邊也是要是建立物件,然後反射獲取所有欄位資訊,由於不能建立物件,直接報錯了。 就這麼結束了嗎?怎麼可能,還沒下班呢,接著走下去。。。。 現在我開始懷疑這個class是不是有問題,然後就開始用arthas的另一個命令jad來反編譯。 通過jad 命令將 JVM 中實際執行的 class 的 byte code 反編譯成 java 程式碼,便於我們理解業務邏輯,也能讓我們知道程式碼跟本地的到底是不是一致。 **jad --source-only ****類全路徑** 執行完後,什麼也沒輸出,我一度懷疑這個命令是不是我用錯了,然後我試了下jad --source-only java.lang.String 發現命令沒問題,就是那個class有問題。 這時我想起還有一個redefine命令可以用於載入外部的.class檔案,看看能不能載入進來。於是我將lib目錄裡面依賴的jar包解壓了,然後用redefine去載入那個不能反編譯的class。 ![](https://img2020.cnblogs.com/blog/1618095/202003/1618095-20200303100121564-1894264010.png) 居然告訴我是一個無效的class,嘗試多次都無法讓這個class現出廬山真面目。 最後沒辦法,只能將這個class弄到本地,拖入IDEA中反編譯,對比了下程式碼,跟git倉庫裡面的一模一樣,也就不存在jar包損壞的問題。 # 即將揭開真相 到目前為止,有效的線索如下: * class已載入,但是無法例項化 * 通過本地反編譯,程式碼是完整的 越在這種沒有思路的情況下越要靜下心來思考,於是再次看了一遍原始碼,發現這個類中有引用一個外部的自定義異常類。 然後我用sc -d去檢視這個類的資訊,告訴我不存在,終於明白了。 ![](https://img2020.cnblogs.com/blog/1618095/202003/1618095-20200303100135562-1873910512.png) 看上面這張圖,專案A依賴了API,API中依賴了Common,Common中又依賴了很多其他的三方Jar包。 由於專案A和Common中依賴的三方Jar包衝突了,所以專案A中之前就簡單粗暴的把Common給排除了,衝突是解決了。 在進行RPC呼叫的時候,請求的資料響應回來後需要反序列化成物件,這個時候去建立物件失敗了,因為類中依賴了某個外部的類,但在當前專案中沒有載入進來,所以就報錯了。 # 總結 這次的問題歸根到底還是沒有想到一個API會依賴其他的模組,本身API作為RPC呼叫客戶端就應該簡潔。 其實在做exclusion的時候應該只exclusion有衝突的三方Jar,不應該將整個Common都exclusion掉。 最後就是合理的利用方便快速的工具幫助我們快速的排查問題,arthas就是這個好幫手,通過arthas我們可以進一步排除程式啟動後加載的class有沒有問題,進一步縮小範圍。