優雅的給線上環境打補丁 頂 原

場景

在線上往往會遇到一些比較尷尬的異常,例如空指針。這種操作往往是某些情況校驗不完善,客戶輸入了各種奇怪的內容導致的。當遇到這種情況的時候,修改都很方便,但是如何更新到線上是個問題了。爲一個小問題,重新更換環境就動作有點大了,還得晚上派人值守。

更新方式

我們主要利用了2中java的外掛技術來完成這種不重啓更新環境。這兩種技術分別是javaagent以及 Vitural Machine attach。attach主要是爲了把javaagent給attach到目標進程上。javaagent裏主要寫類的重新轉化工作。

public class Transform implements ClassFileTransformer {

	@Override
	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
			ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

		if (className.contains(Main.CLASS_NAME)) {//過濾類
			byte[] readAllBytes = null;
			try {
			//讀取修改後的字節碼
				readAllBytes = Files.readAllBytes(Paths.get("E:\\delete\\Nep.class"));
			} catch (IOException e) {
				e.printStackTrace();
			}
			return readAllBytes;
		}
		return null;
	}

}

當類加載或者重新轉化的時候會經過ClassFileTransformer的 transform方法,返回null表示字節碼沒有任何修改,如果返回一個新的數組,就可以替換加載到內存的字節碼了。

	public static void agentmain(String args, Instrumentation ins) throws UnmodifiableClassException {
		//第二個參數表示是否能重新定義
		ins.addTransformer(new Transform(), true);
		List<Class> transformClass =new ArrayList<Class>();
		Class[] allLoadedClasses = ins.getAllLoadedClasses();
		for (Class clazz : allLoadedClasses) {
			if (clazz.getName() != null && clazz.getName().contains(CLASS_NAME)) {
				transformClass.add(clazz);
			}
		}
		if(!transformClass.isEmpty()){
			ins.retransformClasses(transformClass.toArray(new Class[0]));
		}
}

agentmain是一個入口函數。當agent加載以後,會從這裏執行。Instrumentation 可以幫我們獲取到所有加載到內存的類。然後根據要求篩選出合適的類進行重新轉化。(省略掉attach代碼) 利用這種方式。我們就可以把編譯好的字節碼準備好,然後以文件流的形式讀取到內存中,進行動態的替換類的實現。

適用場景

重轉換可能會更改方法體、常量池和屬性。重轉換不得添加、移除、重命名字段或方法;不得更改方法簽名、繼承關係。在以後的版本中,可能會取消這些限制。在應用轉換之前,類文件字節不會被檢查、驗證和安裝。如果結果字節錯誤,此方法將拋出異常。

這裏是jdk文檔裏說明的,你的類如果是加了字段,那就不行了。文檔中說可能會取消這些限制,現在是可以新增或刪除方法,必須是private static以及private final。替換的過程會STW。這個過程非常短暫,相比替換版本而言,這個已經是代價非常小了。

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