hotswapagent——熱更新程式碼而無需重啟生產環境
coding階段,我們習慣於以debug模式執行程式,享受即時更新程式碼而無需重啟服務的高效開發速度。
遺憾的是,無論是ide的debug模式,還是jdk1.4+的instrument機制,都只能做到修改方法體的熱更新。如果你新增或刪除了某些method或field,馬上就會報熱更失敗的提示。
那麼有沒有什麼高大上的工具能夠支援動態修改類結構呢?答案是肯定的。
JRebel——一個jvm外掛,允許開發者隨時修改程式碼、資源而無需重啟服務。在eclipse商店裡就有一個JRebel的外掛。但JRebel是收費軟體,當然,網上也有破解版。
本文將介紹另外一個免費工具——HotswapAgent+DCEVM組合。
The Dynamic Code Evolution Virtual Machine (DCEVM) 是一個jvm級別的補丁,安裝過程會替換jvm自身的檔案。DCEVM的官網最後一次更新是為了相容jdk1.7,對於最新的jdk1.8則無能為力。
HotswapAgent是一個git開源專案,github地址-->HotswapAgent。專案上最新的DCEVM是支援jdk1.8的。
在官網上下載DCEVM-light.jar和hotswap-agent.jar 兩個jar包並安裝DCEVM後,就可以開始我們的探索之旅了。
1.編寫實體類
2.編寫測試入口public class Person { public String toString(){ return "from person,hello"; } }
3.以Eclipse開發工具為例,配置vm引數,然後以debug模式啟動程式public class Entry { public static void main(String[] args) throws Exception { final Person p = new Person(); //記憶體只有一個例項物件 new Thread( new Runnable(){ @Override public void run() { while(true){ try{ Thread.sleep(1000); System.err.println(p); }catch(Exception e){ } } } } ).start(); } }
vm引數(-XXaltjvm=dcevm -javaagent:E:\workspace\jar_libs\hotswap-agent.jar),如下所示
啟動後,看到終端輸出如下提示,就說明環境配置成功
4.程式啟動後,修改實體類並儲存程式碼(ide要開啟自動編譯)
public class Person {
private final int age = 3; //動態加欄位,這裡要加final修飾
public String toString(){
testAddMethod();
return "from person,hello,age="+age; //動態修改方法體
}
public void testAddMethod(){ //動態加方法
System.err.println("hot update ,add method succ.......");
}
}
5.執行結果如下,可以看到,熱更成功了!!!!!!6.雖然以debug模式熱更新成功,但在生產環境一般都不是debug模式,那麼在生產環境應如果部署呢?
其實也是相當簡單,只需要稍微修改一下vm引數,命令列執行引數為
java -XXaltjvm=dcevm -javaagent:E:\workspace\jar_libs\hotswap-agent.jar=autoHotswap=true Entry
在執行過程中,使用已編譯後的新class檔案去替換舊class檔案,即可看到熱更效果。
7.若專案是以可執行jar包的方式部署,只需在編譯後的class根目錄裡面新建一個檔案hotswap-agent.properties,然後設定extraClasspath為指定的更新路徑,同時設定autoHotswap=true,將需要更新的檔案放到extraClasspath目錄下,即可更新指定檔案。
8.最後說一下使用hotswapagent的一些限制。
官網上說Hotswap除了無法修改類的層次關係(例如改變父類或移除介面),其他熱更操作均可實現。但從我個人的測試方法來看,官網的說法還是有些誇大成分。如果真的是要在生產環境上使用此方法進行熱部署,強烈推薦一定要先在本地環境試驗一番,確保有效才能放到生產環境。
自己隨便測試了一番,發現以下操作無法熱更成功,也有可能是我使用方法有誤(^_^)
1.增加例項屬性並賦值,列印後發現賦值無效,輸出的是型別的預設值,使用final常量則能生效。應該是由於例項初始化過程已經結束,不再走例項的屬性初始化邏輯。
2.修改匿名類方法體裡用到的例項引用,程式直接宕機停止執行,示例程式碼如下
報錯異常為
<span style="font-family:SimHei;">HOTSWAP AGENT: 15:19:8.283 RELOAD (org.hotswap.agent.config.PluginManager) - Reloading classes [Entry, Entry$1] (autoHotswap)
HOTSWAP AGENT: 15:19:8.316 RELOAD (org.hotswap.agent.plugin.jvm.AnonymousClassPatchPlugin) - Class '/Entry' has been enhanced with anonymous classes for hotswap.
Exception in thread "Thread-3" java.lang.NoSuchFieldError: val$p
at .Entry$1.run(Entry.java:18)
at java.lang.Thread.run(Thread.java:745)</span>
使用反編譯工具檢視匿名類的程式碼發現,報錯的引用"val$p"指向的是原匿名類的引用,報錯原因暫不詳
估計是因為匿名類的緣故。匿名類,內部類一直是熱更的軟肋。我所接觸的熱更方式都要求程式碼儘量不要出現內部類,匿名類。
總結:
hotswapagent給我們的開發過程帶來了無與倫比的快感,想象一下,在開發執行過程就可以隨意增減方法,隨時隨地進行程式碼重構,這該有多爽啊。但由於該工具仍處於alpha版本,穩定性無法考量,我曾發郵件諮詢過作者,作者也建議不要輕易放到生產環境!!Anyway,在生產環境能夠修改方法體,已足矣。。。