Java 9、OSGi以及模塊化的未來(第二部分)

AlejandraSt 7年前發布 | 12K 次閱讀 Java9 Java開發 JBoss OSGi

核心要點

  • Java 9會在2017年發布,一個標志性的特性就是新的模塊化系統,名為Java平臺模塊化系統(JPMS)。本文探討了它與現有的Java模塊標準即OSGi會產生什么樣的關聯,又會對其產生什么樣的影響。
  • 自1.0版本以來,Java已經增長了20倍,對這個平臺進行模塊化是非常必要的。為了解決這個問題,也曾有過很多失敗的嘗試。而與此同時,OSGi為開發人員提供應用程序模塊化的功能已經有16年之久了。
  • OSGi和JPMS在實現細節上有本質上的區別。如果將JPMS作為模塊化的通用解決方案,似乎會有嚴重的缺陷和缺失的功能。
  • JPMS的目標是使用起來比OSGi更簡單、更容易。但是,讓現有的非模塊化產品模塊化是非常復雜的,而且JPMS在這個目標上似乎并沒有成功。
  • JPMS在Java平臺自身模塊化方面做得很好,這意味著我們可以為特定的工作構建一個小的運行時環境,它只包含Java平臺相關的部分。在應用程序模塊化方面OSGi有很多優勢。我們已經證明了兩者可以結合起來,這看起來是一個成功的方式。

本文是“Java 9、OSGi以及模塊化的未來”系列的第二篇文章。

我們將會繼續深入討論OSGi與Java平臺模塊化系統(JPMS),后者計劃將作為Java 9的一部分而發布。在第一部分中,我們在整體上對比了這兩個模塊化系統,描述了它們各自是如何解決在模塊間進行隔離這一問題的。我們深入研究了依賴功能是如何運行的,并了解了一些反射方面的問題。在第二部分中,我們將會討論版本化、動態模塊加載以及未來OSGi和JPMS實現互操作的可能性。

版本化

版本化在軟件交付中是很重要的一個方面。API和實現都會發生變化,所以,當我們依賴它們的時候,實際上我們所依賴的是它們在某個時間點上的狀態。所有的模塊化系統都必須要解決這一現實問題……在引用制件(artifact)或依賴項時,這一般會通過在這兩者上面明確聲明版本來實現。

但是,并非所有的變更都具有相同的破壞性。如果我們基于1.0.0版本的某個模塊來構建和測試軟件的話,那么如果部署依賴的1.0.1或1.0.5版本的話,我們的軟件應該也能正常運行……但是,如果我們部署這項依賴的2.0.0版本或5.2.10版本,那么我們的軟件很可能就不能正常運行了。這表明,一個模塊化系統需要能夠理解和支持兼容性范圍的功能。

OSGi一直就支持這些理念。bundle和導出包都是版本化的。導入包指定的是一個范圍,這個范圍通常會包含較低的邊界而不包含較高的邊界,比如[1.0.0, 2.0.0),它代表了從1.0.0到2.0.0的所有版本,但是不包含2.0.0版本本身。OSGi使用語義化版本(semantic versioning),完全遵循流行的 語義化版本規范 (盡管OSGi本身要比這個文檔更早)。大致來講,版本號中的第一部分是主版本(major),代表了在功能和API方面的破壞性變更,第二部分是次版本號(minor),表明了這是非破壞性的功能增強,而第三部分是微版本號(micro),代表了已有功能的補丁。

OSGi開發人員不需要關心或明確聲明這些版本范圍。就像導入功能本身一樣,版本范圍也是在構建的時候通過分析依賴關系自動生成的。例如,如果我們只是作為消費者來使用API包,那么我們有可能使用一個很寬泛的范圍,比如[1.0.0, 2.0.0),所有的次版本和微版本都包含在內。但是,如果我們要作為提供者實現服務接口的話,那么我們必須要以更狹窄的范圍來導入包,比如[1.0.0, 1.1.0),意味著所有從1.0.0到1.1.0的版本,但是不包含1.1.0版本本身。這里的區別在于支持1.0.0版本功能的提供者將不會支持1.1.0版本,因為次版本號增加的數字表明服務提供者無法自動提供新的功能。而另一方面,對于服務的消費者來說,可以很容易地使用1.1.0或1.2.0版本,它們只需忽略掉新增的功能就可以了。

除了在導入時生成版本范圍,OSGi構建工具( bnd )還能幫助我們保證導出包的正確性。版本是包的一個屬性,可以使用 @Version 注解直接寫入到包的 package-info.java 文件中。在這里很重要的一點就是當包的內容發生變化的時候,要及時更新這個版本號:例如,如果我們為服務接口新增了一個方法,那么我們需要將版本號從1.0.0增加到1.1.0。構建工具會檢查版本號,使其能夠精確地反應變更的實際情況。舉例來說,如果我們新增了方法,但是忘記了變更版本號,或者只是對版本號做了很小的變更,比如只增加到1.0.1,那么構建將會失敗。

最后,OSGi的靈活性還在于它允許將某個模塊的多個版本同時部署到一個應用之中。如果我們的一些依賴項具有對某些通用庫的傳遞性依賴的話,比如slf4j或Guava,就有可能發生這種情況。這里也有一些限制,我們不能在一個模塊中直接導入某個包的多個版本,但是當我們真正需要這項特性的時候,它還是非常有價值的。

這些都意味著OSGi提供了一種綜合性的方案,允許我們由單獨的團隊或組織來構建模塊,稍后再將這些模塊組合成一個應用。這些工具能夠讓我們充分相信我們所選擇的模塊能夠在一起正常運行起來。

與此形成對比的是,JPMS在版本化基本上沒有提供任何的支持。

在module-info.java文件中,我們沒有辦法指定版本(在編譯后的module-info.class文件中會有一個Version屬性,但是它并不是由Java源碼生成的,它的實際用處尚不明確)。依賴無法進行版本化:JPMS模塊只能通過名稱來聲明對其他模塊的依賴,不能使用版本,當然就更不能指定版本范圍了。這些特性需要通過外部工具來提供,但是在這方面的努力也是困難重重,因為module-info.java源文件無法進行擴展,在這個文件中無法使用Java注解。

JPMS的需求文檔表明,選擇運行期版本兼容的組件并不在它們的功能范圍之內。這意味著必須要由其他的工具來完成這項任務,但是如果沒有合適的元數據的話,這些工具也是無法實現的。其實,如果能將版本元數據和基本的模塊元數據放到同一個描述文件中,這是非常自然合理的,但是目前來看,這將無法實現。

同時,正如我們前文所述,在JPMS中,并不允許同一個模塊的多個版本共存。另外,它還不允許多個模塊導出相同的包,甚至不允許私有包出現重疊。所以,用來構建合法模塊集的工具必須要找到一種解決傳遞性依賴的方式。在很多場景下,所謂的“解決方案”不過是讓特定的模塊不與其他的模塊一起使用。

動態化

OSGi基于類加載器實現了隔離,這種機制有一個很好的副作用,那就是能夠支持運行時動態加載、更新和卸載模塊。在企業級的環境中,這似乎并不是那么重要,大多數企業級部署的OSGi應用其實并沒有使用動態更新的功能。OSGi也沒有說我們必須要使用動態更新!

但是,動態部署在其他的環境中就非常有價值了,比如說在IoT領域。如果軟件部署到了成千上萬,甚至百萬級數量的設備上,通過緩慢且斷斷續續的網絡對軟件進行更新是一件非常令人頭痛的事情。OSGi是為數不多的能夠在任意平臺上直接支持運行期更新的技術,它所使用的數據量絕對是最少的:我們只需要發送真正發生變更的模塊即可。

最初在2000年,電信運營商都愿意借助OSGi在家庭網關和路由器上構建智能家居解決方案,采用這種方案一個主要原因就在于它能夠在無需固件升級的情況下管理軟件。固件升級并不是一個很有吸引力的解決方案,主要的原因在于:下載——固件升級一般需要下載MB大小級別的軟件,而且可能需要下載到上百萬臺的設備上。固件是與設備相關的,所以最終可能需要創建很多不同的更新包,并要管理它們的部署;測試——固件升級需要廣泛、耗時、成本高昂且壓力重重的測試,因為每次都需要在所有的設備上進行測試。OSGi能夠非常顯著地簡化這一過程,更新可以應用到模塊中,在運行中的網關和路由器上進行安裝,不需要重啟。相同的模塊可以應用到所有的設備上(它通常會從底層的設備硬件層抽象出來),另外很重要的一點,單元測試可以只針對更小的一個軟件集來執行,從而節省大量的時間、精力和金錢。一個很具體的例子就是Qivicon,它是由德國電信所創建的行業聯盟。Qivicon所提供的家庭網關包含了基于OSGi的軟件技術棧、后端基礎設施、針對應用開發人員的工具,并且還會提供維護和支持。通過采用OSGi來支撐其生態系統,Qivicon合作伙伴能夠更加快速地將他們的智能家居產品推向市場。

Qivicon的合作伙伴會持續地集成新設備,開發具有創新性的增值服務。這需要復雜的設備管理和軟件供應能力,以確保特定設備平臺中軟件組件的依賴和兼容性管理能夠正常運行。在OSGi中,這些功能已經借助已有的工業標準進行了標準化,比如 TR-069 和 OMA-DM 。

除此之外,動態化行為相關的功能并不僅僅局限于軟件更新方面。

OSGi服務注冊中心(OSGi Service Registry)本質上也是動態的。服務可以來去自如,綁定服務的組件能夠實時感知。借助服務,我們能夠表述和報告持續變化的外部現實世界。即便是在相對穩定的企業級應用中,這也是相當有意義的。例如,OSGi服務可以反映外部數據feed的可用性或者具備負載均衡功能的REST服務的IP地址,甚至是證券市場的開放時間。消費服務的每個組件能夠決定當服務不可用的時候要采取什么樣的反應動作:它可以繼續運行,也可以將自己所擁有的服務解除注冊。這樣的話,低層級的狀態變更能夠非常可靠地傳遞到它們所影響的地方。

互操作性與未來

2017年,JPMS會隨著Java 9的發布,在Java的主版本中釋放。目前已經有大量使用OSGi所編寫的應用,并且還有很多正處于編寫之中。它們是安全的嗎,這些應用必須要針對新的JPMS模塊系統進行重寫嗎?

需要闡明的第一點就是 OSGi應用不經任何變更就能運行在Java 9上 ,只要應用編寫的時候沒有用到不支持的、內部Java API即可。這同時也是針對所有Java代碼的通用建議。OSGi只用到了受支持的Java API,Oracle給出了一個鄭重的承諾,不會破壞這樣的應用。在使用Java 9中所遇到的問題很可能是因為你所用到的庫使用了JDK的內部類型,在Java 9中只有借助特定的配置標記才能繼續使用它們。OSGi的用戶對這種變更準備得會更加充分,因為他們的導出會進行明確的聲明。普通的應用在聲明依賴的時候只是將JAR文件堆砌到類路徑下,與之對比,在OSGi平臺上構建的應用對它的依賴范圍會更加清晰。

在這種最基本的兼容模式下,OSGi框架和bundle將會存在于一個“未命名”的JPMS模塊中。OSGi將會繼續提供已有的隔離特性,還有它強大的服務注冊和動態加載功能。你在OSGi方面的投資依然是安全的,并且對于新項目來說OSGi依然是一個很棒的選擇。

但是,我們希望能夠做得更好一些。當OSGi運行在模塊化的Java 9平臺中,我們應該能夠使用平臺中的模塊。例如,某個OSGi bundle可能會聲明它所依賴的平臺模塊集合——也就是說,我們應該能讓OSGi bundle直接聲明對JPMS模塊的依賴。OSGi框架應該能夠在運行時遵循這些依賴,工具應該基于這些依賴準備運行時環境。

到這里,所有的事情看起來都非常棒。在2015年11月份的 一篇博客文章中 ,我描述了一個我所構建的概念驗證樣例,闡述了OSGi如何運行在JPMS上。我詳細介紹了OSGi bundle可以如何聲明對特定JPMS模塊的依賴,這些模塊運行在基礎平臺中。我展示了如果bundle所依賴的JPMS模塊沒有位于平臺之中,OSGi將會拒絕這個bundle。我沒有構建用于組裝運行時的原型工具,但是創建這種工具所需的各項部件都已經存在了。

圖三展示了未來互操作性是如何運行的。我們可以看到Bundle A導入了 javax.activation 包,這個包是JPMS中的 java.activation 模塊所導出的。互操作層將會知道平臺包含這個模塊,允許OSGi解析它。Bundle A不需要任何變更就能遷移到Java 9上。Bundle B使用了 java.httpclient JPMS模塊的 java.net.http包 ,但是這個包不能表達為OSGi Import-Package,因為它是以“ java ”開頭的(注意,所有的bundle和模塊都隱式地依賴 java.base 包)。

因此,我們提議了一個新的OSGi頭信息,名為“Require-PlatformModule”,它表達了所需的JPMS模塊。這樣的話,如果平臺不包含java.httpclient模塊的話,OSGi框架將會讓Bundle B“快速失敗(fail fast)”。它也能讓工具為應用構建一個完整的運行時,在這個運行時中只需使用最少的JPMS模塊和OSGi bundle。

再次強調,很重要的一點就是這些工作只是一個非官方的概念驗證,OSGi與JPMS將會按照什么形式進行互操作還要取決于規范如何制定。

圖:OSGi – JPMS互操作概念原型

結論

從Jigsaw原型項目來看,JPMS在模塊化Java平臺方面做得非常不錯。這項工作所帶來的一個結果就是我們可以構建一個很小的運行時環境,其中只包含特定負載所需的Java平臺中的內容。

但是,如果作為應用層級的模塊化規范,那JPMS就有一些很嚴重的不足了。缺少版本化這項不足會讓人覺得有些驚訝,如果沒有外部工具提供元數據,組成并行的系統,我們很難想象該如何構建實際的應用。整個模塊的依賴聲明將會引入傳遞性的依賴,這可能會超出實際所需,從而削弱構建更小平臺所帶來的收益。反射無法訪問非導入的包,在使用Java生態中已有的框架時,這一點可能會帶來不必要的麻煩。

對于JDK本身來說,這些設計可能是非常恰當的:它們會增加平臺的健壯性和安全性,避免破壞已有應用的向后兼容性。但是,這也有一定的代價,對于應用模塊化來說,這不是一個好的選擇。

所以,OSGi的前景看起來依然很光明:通過將OSGi與一個裁剪過的模塊化Java平臺組合在一起,我們能夠同時得到兩者最好的一面。在16年的經驗中,OSGi遇到并解決了很多問題,而這些問題是JPMS甚至還沒有遇到過的。OSGi生態系統的工具和運行時非常廣泛和深入。它保證不會過時,會支持長期維護的、已證明可行的、獨立的標準。你還在等待什么呢?

 

 

來自:http://www.infoq.com/cn/articles/java9-osgi-future-modularity-part-2

 

 本文由用戶 AlejandraSt 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!