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

2020 年寫了很多事故解決的文章,並不是我絞盡腦汁想出來的,而是真的遇到了這些問題。通過文章的方式記錄下來,分享出去,纔有意義。

事故背景

首先看下面的圖吧,這是我從 cat 上截的圖。


可以看到是一個 Rpc 調用的錯誤,從錯誤中我們只能分析出這個 Rpc 的請求成功了,並且返回了,因爲都走到了反序列化這步。

最後是在創建 DTO 對象的時候報錯了,Could not initalize class xxxxx.DTO說明了這一點。

作爲一個調用方,雖然看到了明確的錯誤,但還是要本着嚴謹的態度去排查問題,還是先確認服務提供者到底有沒有問題,跟同事確認了,服務提供方沒問題,通過 telnet 可以正常 invoke。

好了,到這爲止就把背景交代清楚了,能不能將這個潛藏的 Bug 找出來就各顯身手吧。

arthas 大顯身手

要想效率高,那必須得有好用的工具呀!arthas 挺身而出,都毛遂自薦了,不用白不用。

首先使用 sc 命令查看 JVM 已加載的類信息,就看這個不能實列化的類到底有沒有被成功加載。

sc -d 類全路徑 (打印類的詳細信息


類的信息都被打印出來了,足以證明這個類被加載了。

然後打印下類裏面的字段,看看有沒有丟失什麼的

sc -d -f 類全路徑 (打印類的Field信息


居然報錯了,錯誤還跟我們之前在 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。


居然告訴我是一個無效的 class,嘗試多次都無法讓這個 class 現出廬山真面目。

最後沒辦法,只能將這個 class 弄到本地,拖入 IDEA 中反編譯,對比了下代碼,跟 git 倉庫裏面的一模一樣,也就不存在 jar 包損壞的問題。

即將揭開真相

到目前爲止,有效的線索如下:

  • class 已加載,但是無法實例化

  • 通過本地反編譯,代碼是完整的

越在這種沒有思路的情況下越要靜下心來思考,於是再次看了一遍源碼,發現這個類中有引用一個外部的自定義異常類。

然後我用 sc -d 去查看這個類的信息,告訴我不存在,終於明白了。


看上面這張圖,項目 A 依賴了 API,API 中依賴了 Common,Common 中又依賴了很多其他的三方 Jar 包。

由於項目 A 和 Common 中依賴的三方 Jar 包衝突了,所以項目 A 中之前就簡單粗暴的把 Common 給排除了,衝突是解決了。

在進行 RPC 調用的時候,請求的數據響應回來後需要反序列化成對象,這個時候去創建對象失敗了,因爲類中依賴了某個外部的類,但在當前項目中沒有加載進來,所以就報錯了。

總結

這次的問題歸根到底還是沒有想到一個 API 會依賴其他的模塊,本身 API 作爲 RPC 調用客戶端就應該簡潔。

其實在做 exclusion 的時候應該只 exclusion 有衝突的三方 Jar,不應該將整個 Common 都 exclusion 掉。

最後就是合理的利用方便快速的工具幫助我們快速的排查問題,arthas 就是這個好幫手,通過 arthas 我們可以進一步排除程序啓動後加載的 class 有沒有問題,進一步縮小範圍。

技術交流可以掃下面圖片加我微信。

熱文推薦

驚訝!緩存剛Put再Get居然獲取不到?

好機會,我要幫女同事解決Maven衝突問題

上線前一個小時,dubbo這個問題可把我折騰慘了

爲了控制Bean的加載我使出了這些殺手鐗

如有收穫,點個在看,誠摯感謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章