Java内部具有原子更新的动态热交换环境
有人可能會說上述標題可以簡稱為OSGi ,我想在一開始就放棄這種思考過程。
對于OSGi而言,這不是一個冒犯,這是一個很棒的規范,在實現層或可用性層上都弄得一團糟,這就是我對OSGi的信念。 當然,您可以使用OSGi進行此操作,但同時還要進行一些自定義工作。 使用OSGi解決此問題的缺點是在開發過程中引入了不必要的復雜性。 我們受到了JRebel的啟發,并且想了一會兒,我們想要并很快意識到那條線上的東西,我們不想在生產級運行時進行字節碼注入。 因此讓我們分析問題領域。
問題域
我們要解決的問題與UltraESB有關 ,具體來說就是實時更新功能 。 UltraESB支持以原子方式向正在運行的ESB更新/添加新的配置片段(稱為“ 部署單元 ”),而不會造成停機時間,最重要的是不會出現任何不一致的狀態。 但是,此功能的局限性之一是,如果特定的Java類駐留在用戶類空間中,則需要對此部署單元的配置更新進行更改,因此需要重新啟動JVM。 盡管在集群部署中(使用循環重啟)可以承受,但是在單實例部署中,這給整個系統帶來了停機時間。
我們必須確保在解決這一問題時幾乎沒有任何保證;
- 在更新部署單元時,該單元已經接受的消息應使用所有資源,包括現有單元的已加載類(以及要加載的任何新類),而任何新消息(在完成單元更新后)都必須使用被調度到新的部署單元配置和資源庫,我們稱之為“ 一致性保證 ”。
- 為了確保這一點,我們需要在同一JVM上管理同一類的2個(或更多個)相同類的版本,以使各個部署單元以原子方式使用這些類。 讓我們稱其為部署單元的“ 原子性保證 ”。
- 部署單元配置可能包含Java片段,這些Java片段在更新時進行即時編譯,其中可能包含對更新后的類的依賴關系,從而使編譯器能夠找到該類的新版本以進行編譯過程。 這就是“ 正確性保證 ”
- 更新過程必須對用戶透明(他們不必擔心這一點,無論是在開發時間上還是在部署時間上),并且整個過程應該很簡單。 讓我們稱之為“ 簡單性保證 ”
現在您將理解這個問題不僅僅是OSGi,因為編譯是至少在我撰寫此博客時OSGi無法自行解決的問題(AFAIK)。
如果我回到OSGi,以確保它非常清晰,為什么我們不走這條路呢?讓我們詳細分析需求。 我們真正想要的不是完全模塊化的JVM,而是JVM內部可動態且原子可重新加載的特定空間。 將其映射到我們的實際用例中, 可以確保用戶寫入并插入ESB的任何內容(即包含代理服務,序列和中介邏輯的部署單元)都可以動態地自動重新加載,可版本化,但不能像在ESB核心中執行用戶代碼 。 這是用戶向我們詢問的內容,而不是您如何在不重新啟動的情況下如何在運行時向ESB添加其他功能。 我同意能夠添加新功能很酷,但是似乎沒有人希望這樣做以及與之相關的復雜性。 我們已經準備好進行任何復雜的工作,但是還沒有準備好將這種復雜性或任何復雜性傳遞給我們的用戶。
擬議的解決方案
如果您想在Java中使用前2個保證“ Consistency / Atomicity ”(能夠在運行時加載同一個類的2個版本,并在其中的正確任務中使用正確的類),則除了編寫之外,別無他法一個新的類加載器,它強制JVM執行子優先級加載 。 JVM標準類裝入器都是“父優先”的。 典型應用程序容器的WebAppClassLoader與我們想要的非常接近,但是在生產環境中具有動態重載功能。 舊類空間和新類空間應由該類加載器的2個實例管理,以便能夠安全地隔離這2個版本。
要了解上述事實,重要的是要了解JVM如何識別類。 即使從Java語言的角度來看,類也是由FQN唯一標識的,即FQN,即“包名+類名”,但是從JVM的角度來看,除了上述概念外,已經加載了該類的類加載器也是一個事實班級的獨特性。 在類似OSGi的環境中,這就是為什么即使您強制轉換為正確的類型也看到ClassCastException的原因。 因此得出的結論是,我們需要編寫一個類加載器,并為可重新加載的不同部署單元的不同版本保留該類加載器的單獨實例。
為了確保即時編譯器看到正確的類來編譯序列片段,從而保證“ 正確性 ”,需要有一個JavaFileManager實現,再次尋找更新的類空間。 Java編譯器任務javac通過指定的文件管理器(作為JavaFileObject實例)而不是通過類加載器(作為類對象)來搜索依賴項以編譯類,這是為了確保編譯器有效地解析類,因為可能存在依賴項在正在編譯的類中。
此外,不應該要求用戶將jar文件放在經過版本控制的文件結構中,以免影響對“ 簡單性 ”的保證,而ESB本身必須管理該jar文件的版本控制以確保我們不會混合使用不同的版本類空間。 這對于在不同版本中正確執行編譯器任務也很重要,因為編譯器使用“ 內存映射”文件讀取文件管理器提供的類的輸入流上的類定義,從而強制維護每個文件的物理副本。 jar文件/類的版本。
執行執行
首先讓我指出完整的變更集 ,在閱讀實現時可以不時參考。
我們已經確定了3個要實現的關鍵空間,其中第一個是提供用戶類空間的類的類加載器。 我們將其命名為HotSwapClassLoader (我不會在博客中向您顯示代碼片段,請記住該完整代碼,請牢記AGPL許可的條款,因為該代碼為AGPL )。 現在,我們想將此類加載器與部署單元的版本相關聯,UltraESB固有地支持該版本加載器,因為它將它們保持為單獨的Spring子上下文。
因此,任何新的部署單元配置創建(包括現有部署單元的新版本)都將實例化此類加載器的新實例,并將其用作部署單元配置的資源/類加載器。 初始化時的類加載器計算用戶類空間的規范化哈希值,并檢查當前版本的類空間是否存在現有副本,并根據上述聲明使用該副本或創建新副本。 這種散列和重用類空間的現有副本會阻止管理同一用戶類空間版本的2個副本,因為整個過程都是在靜態最終鎖上同步的。 然后,它對用戶類空間的該副本進行操作。 必須進行此復制,以免讓用戶擔心類的版本控制,并確保在給定的配置中使用了正確的類集。 該類裝入器還采取了廣泛的措施,以確保盡早清除類空間副本。 但是,這只能保證最終清除。
執行的下一個主要項目是InMemoryFileManager哪個是哪個得到改性通過列表的方法的一個可迭代以支持另外的用戶類的空間來在內存中的編譯源代碼片段現有類SwappableJavaFileObject實例。 文件管理器首先查詢HotSwapClassLoader,以查找與用戶類空間相對應的SwappableJavaFileObject實例,然后是系統類空間,并以WrappedIterator的形式返回,以確保用戶空間類獲得優先級。
在實現的最后一步中,對核心JVM功能進行了調整/自定義之后,只需使用此自定義類加載器來加載序列和代理服務的類,并為片段編譯任務提供自定義文件管理器即可。部署單元以完成解決方案。 我們還希望有一個開關在默認情況下將其禁用,并建議在生產部署中將其禁用。 為了簡化運行時環境并進行其他一些自定義,UltraESB引入了環境概念,該概念是從Grails環境功能中借用的。
最后,成功實現了動態運行時,即“一致”,“原子”,“正確”,最重要的是“對用戶簡單”。
操作行為
現在,我們已經實現了解決方案,讓我們看一下UltraESB的內部結構,了解它在生產部署中如何工作。 發出配置添加或更新管理命令后,將更新生產環境中的所有部署單元配置。 可以通過原始JMX或通過在JMX操作之上實現的任何管理工具(例如UTerm )或通過UConsole發出此命令。
完成此實現后,它不會對您進行更新的方式進行任何更改,它通過添加/替換jar文件的功能進一步增強了功能,并進行了修改,從而將更新影響到UltraESB的lib / custom用戶類空間中,從而確保了在更新后發出每個所述管理命令后,為新配置選擇更新的jar文件/類。
您可以在UltraESB的夜間版本中嘗試此操作,甚至可以等待2.0.0版本的發布,該版本計劃在2013年1月中旬發布更多新的酷但可用的功能。
參考: Java內部的動態熱交換環境 ,我們的JCG合作伙伴 Ruwan Linton在
Blind Vision –來自軟件工程與生活博客。
翻譯自: https://www.javacodegeeks.com/2012/12/dynamic-hot-swap-environment-inside-java-with-atomic-updates.html
總結
以上是生活随笔為你收集整理的Java内部具有原子更新的动态热交换环境的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win10驱动安装失败原因介绍(win1
- 下一篇: 跑步机设置(跑步机设置多少)