記一次使用 Arthas 熱更新線上程式碼(誤)
起因
在一次迭代中,出現了一個低階錯誤,if
語句中的判斷邏輯出現了錯誤,剛好這個功能場景在開發和測試過程中很少觸發,使用的使用者也不多,不過的確影響到了少部分使用者,所以還是需要進行修復。
想著只是更新一行程式碼,如果走正常的釋出流程,需要通過以下步驟:
提交程式碼 -> 提測打包 -> 測試環境驗證 -> Release 環境驗證 -> 預發環境驗證 -> 線上環境
如果你的應用體積不小,而且線上機器很多,花費的時間可能足夠喝很多杯 Java
:-O
Arthas
之前使用過 Alibaba
開源的診斷工具 Arthas
,下圖是官方檔案中提到的功能:
不僅可以用來排查問題,還能夠使用它 redefine
進行熱更新。
剛好之前也看到一篇文章介紹如何進行 一條龍更新
,所以就開始了嘗試,先從本地開發測試開始。
選擇方案
方案一:jad/mc/redefine線上熱更新一條龍
開發時寫下的 java
程式是高階語言,需要通過編譯生成 .class
檔案才能在 jvm
中執行。
所以在一個執行中的程式中進行熱更新,需要先將它使用 jad
[Java decompile]反編譯,修改 .java
檔案後使用 mc
[Memory complile] 編譯出 .class
檔案,最後使用 redefine
命令更新虛擬機器器中的程式。
首先可以跟著教程來一次嘗試
# 反編譯
$ jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
# 修改檔案
$ vim /tmp/UserController.java
# 查詢載入的 ClassLoader
$ $ sc -d *UserController | grep classLoaderHash
classLoaderHash 6bc28484
# 編譯
$ mc -c 6bc28484 /tmp/UserController.java -d /tmp
# 熱更新
$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success,size: 1
複製程式碼
跟著教程 demo
,發現程式碼邏輯被修改,返回我修改後的結果,心裡在狂喜,可以不用喝這些多咖啡!
實際專案中反編譯失敗
但在工作中的專案中使用,發現出現了這個問題:反編譯後的類不完整
檢視通過 jad
命令反編譯後的檔案:
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* ....
*/
複製程式碼
在不完整的檔案中進行修改後,進行 mc
命令編譯,將會提示如下:
[arthas@7281]$ mc -c 6bc28484 /tmp/xxxx.java -d /tmp
Memory compiler error,exception message: Compilation Error
......,please check $HOME/logs/arthas/arthas.log for more details.
Affect(row-cnt:0) cost in 884 ms.
複製程式碼
可以看到,如果有複雜的類,並一定能夠成功反編譯,遭遇了失敗,開始排查原因
反編譯失敗原因
開源的好處的是,大家可以一起參與到其中,提出問題和解決問題,在 github
專案 arthas
的 issue
中,通過關鍵字 jad 反編譯
找到了原因
可能好巧不巧,實際專案中的程式碼,就遇上了這 1% 情況(順便看了其它類,發現這種情況還不少...)
當時對於 jad
機制是有點不瞭解,所以想先通過上面的提示的工具 dumpclass
去精確獲取 java
位元組碼,但奈何有些難用,嘗試了幾遍還是沒能拿下來,於是開始換種思路。
方案二:直接拿到 .class
檔案
既然前面的操作,獲取修改後的 .class
檔案,都是為了最後一步 redefine
所服務,那隻要獲取精確的 .class
檔案就可以了,跳過前面的步驟也可以。
於是與一個前輩討論後,他建議我使用 IDEA
工具編譯後的 .class
檔案
於是將原生程式碼進行修改,進行打包編譯,得到想要的 .class
檔案,然後將這個檔案上傳到測試環境,進行替換。
檔案地址類似下圖:
[arthas@63]$ redefine /tmp/xxxxxx.class
redefine success,size: 1
複製程式碼
出現了成功的提示,同時進行了介面測試,發現執行的程式碼是修改後的邏輯!
總結
通過上面說的,正常來說只需要簡單四步就能進行熱更新
一、使用 jad
反編譯出 .java
檔案
二、編輯檔案,修改邏輯
三、使用 mc
編譯修改後的檔案
四、使用 redefine
熱更新
當然,也可能遇到 jad
反編譯失敗的場景,可以參考方案二,直接拿到修改後的 .class
檔案,然後繼續進行操作。
題外話
標題最後一個字為什麼叫【誤】呢,是因為我經過測試,跟前輩們討論後,在釋出流程規範、 IDEA
提供的 .class
檔案不確定性還有工具誤用的把控上考慮,覺得目前不適合使用,於是還是走了正常的釋出流程。
最後,Arthas提醒您: 診斷千萬條,規範第一條,熱更不規範,同事兩行淚
以後要避免再犯這些小錯誤,恩,換言之,我喝了很多杯 Java
:-(