OSGi使用四問

    沒有什麼技術是萬能的,任何一門技術都有它的適用場景和最佳實踐方法。OSGi不只是一門技術,更多的是一種做系統架構的工具和方法論,如果在不適用的場景中使用OSGi,或者在適用的場景中不恰當地使用OSGi,都會使整個系統產生架構級的缺陷。因此,瞭解什麼時候該用OSGi是與學會如何使用OSGi同樣重要的事情。

    每個系統遇到的業務環境都是不一樣的,筆者不希望以經驗式的陳述去回答“什麼時候該用OSGi”或 “爲什麼要使用OSGi”這樣的問題,而試圖通過以下4個問題的討論和利弊權衡,讓讀者自己去思考爲什麼這些場景適用OSGi


1問:OSGi能讓軟件開發變得更容易嗎?

   不可否認,OSGi的入門門檻在Java衆多技術中算是比較高的,相對陡峭的學習曲線會爲第一次使用OSGi開發系統的開發人員帶來額外的複雜度。

   OSGi規範由數十個子規範組成,包含了上千個不同用途的API接口。OSGi規範顯得這樣龐雜的主要原因是實現“模塊化”本身需要解決的問題就非常多。模塊化並不僅僅是把系統拆分成不同的塊而已—這是JAR包就能做的事情,真正的模塊化必須考慮到模塊中類的導出、隱藏、依賴、版本管理、生命週期變化和模塊間交互等一系列的問題。

   鑑於OSGi本身就具有較高的複雜度,“引入OSGi就能讓軟件開發變得更容易”無論如何是說不通的,小型系統使用OSGi可能導致開發成本更高。但是這句話又不是完全錯誤的,隨着系統不斷髮展,在代碼量和開發人員都達到一定規模之後,OSGi帶來的額外成本就不是主要的關注點了,這時候的主要矛盾是軟件規模擴大與複雜度隨之膨脹間的矛盾。如下圖所示,代碼量越大、涉及人員越多的系統,軟件複雜度就會越高,兩者成正比關係。這個觀點從宏觀角度看是正確的,具體到某個系統,良好的架構和設計可以有效減緩這個比率。基於OSGi架構的效率優勢在這時候才能體現出來:模塊化推動架構師設計出能在一定範圍內自治的代碼,可以使開發人員只瞭解當前模塊的知識就能高效編碼,也有利於代碼出現問題時隔斷連鎖反應。OSGi的依賴描述和約束能力,強制開發人員必須遵循架構約束,這些讓開發人員“不自由”的限制,在系統規模變大後會成爲開發效率的強大推動力。

圖 OSGi對軟件複雜度的影響


    可以用一個更具體的場景來論述上面的觀點,解析OSGi架構如何在開發效率上發揮優勢。有經驗的架構師會有這樣的感受:設計一個具有“自約束能力”的系統架構非常不容易。最常見的情況是設計人員設想得很美好,開發人員在實現時做出來的產品卻不是那樣。大部分軟件公司是通過“開發過程”、“編碼規範”、“測試驅動”,甚至“人員熟練度”來保證開發人員實現的代碼符合設計人員的意圖。這樣即使在開發階段做到符合設計需求,也很難保證日後維護人員能夠繼續貫徹原有的設計思想;隨着開發的時間越來越長,系統最終實現的樣子可能和原有的設計產生越來越大的偏差。在軟件工程中,將這種現象稱爲“架構腐化”。架構的“自約束能力”就是指限定不同開發人員在實現功能的時候,實現方式都是一致的,最好只有唯一一條遵循設計意願的路可走,別的方法無法達到目的。更通俗地說就是,儘可能使程序員不寫出爛代碼。

   舉個最淺顯的例子,如果有開發人員在Web層中使用DAO直接操作數據庫,或者在DAL層直接從HttpSession對象中取上下文信息,這樣的代碼也許能逃過測試人員的黑盒測試,但是顯然是不符合軟件開發基本理論的。前者可能因繞過Service層中的事務配置而出現數據安全問題;後者限制了這樣的DAO就只能從Web訪問,無法重用和進行單元測試。如果項目中出現這樣的代碼,筆者認爲首要責任在架構師,因爲架構師沒有把各層的依賴分清,如果Web層只依賴Service層的JAR包,那麼程序員就無法訪問到DAO,如果DAL層沒有依賴Servlet APIJAR包,那麼程序員就不可能訪問HttpSession對象,這就是一種架構缺乏自約束能力的表現。

   大概沒有哪個架構師會犯上面例子那樣幼稚的錯誤。但是,實際情況也遠比例子中的複雜,甚至有一些問題是Java語言本身的缺陷帶來的,例如,依賴了一個JAR包就意味着能夠訪問這個JAR包中的一切類和資源,因爲JAR包中的內容沒有PublicPrivateProtected之分,無法限制用戶能訪問什麼、不能訪問什麼。更復雜的情況是在引入了同一個JAR包的不同版本時怎麼辦?如果依賴包需要動態變化怎麼辦?使用OSGi一個很重要的目的就是彌補Java中資源精細劃分的缺陷,加強架構的自約束能力。

   雖然OSGi起源於精小軟件佔多數的嵌入式領域,但是在Java SE/EE領域中,對於越龐大的系統,使用OSGi進行模塊化拆分就越能發揮出優勢。在商業上已經有一些使用OSGi控制軟件複雜度增長、延緩架構腐化速度的成功案例,如Eclipse Marketplace,它已經擁有了上千個插件,插件的開發者來自全球各地,技術水平差異很大,插件實現的功能也各不相同,是OSGi讓這些插件基本遵循了統一的架構約束,並且一般不會因爲某個插件的缺陷影響整個Eclipse的質量。


2問:OSGi能讓系統變得更穩定嗎?

   筆者遇到過許多由OSGi框架引發的問題,例如,最典型的ClassNotFoundException異常、類加載器死鎖或者在動態環境下的OutOfMemoryError問題等,這些都是基於OSGi架構開發軟件時很常見的。從這一方面看,使用OSGi確實會增加系統不穩定的風險,所以,在開發過程中團隊中有一兩個深入瞭解OSGi的成員是必要的。

   不過,軟件是否穩定不是隻看開發階段可能出現多少異常就能衡量的,軟件的“穩定”應是多方面共同作用的結果。除了關注開發階段是否穩定之外,還要關注是否能積累重用穩定的代碼,問題出現時能否隔斷連鎖反應蔓延,缺陷是否容易修復等。在這些方面,OSGi就可以帶來相當多的好處,例如:

   OSGi會引導程序員開發出可積累可重用的軟件。我們無法要求程序剛開發出來就是完全穩定的,但可以在開發過程中儘可能重用已穩定的代碼來提升程序質量。大家知道,寫日誌可以使用Log4j,做ORM會引入HibernateJava中有許多經過長期實踐檢驗的、被證實爲穩定的開源項目,這些開源項目的共同特徵是都經過良好的設計,能夠很方便地在其他項目中使用。相對而言,在自己開發項目時很多人沒有注意到要進行可積累的設計。

   一種典型現象是項目中出現一些“萬能的包”,通常名字會是XXXCommons.jarXXXUtils.jar等,這些包中存放了在項目中被多次調用的代碼,但是這樣的包不能叫做可重用包。當這些包越來越大、類越來越多、功能越來越強時,與這個項目的耦合就越緊密,一般也就無法用在其他項目中了。在OSGi環境下,“大雜燴”形式的模塊是很難生存的,如果某個模塊有非常多的依賴項,那麼沒有人願意爲了使用其中少量功能去承擔這些間接依賴的代價。因此設計者必須把模塊設計得粒度合理,精心挑選對外發布的接口和引入的依賴,把每個模塊視爲一個商業產品來對待,這樣才能積累出可重用的模塊,也利於提高程序穩定性。

   基於OSGi比較容易實現強魯棒性的系統。普通汽車壞掉一個輪胎就會拋錨,但是飛機在飛行過程中即使壞了其中一個引擎,一般都還能保持正常飛行。對於軟件系統來說,如果某一個模塊出了問題,能夠不波及其他功能的運作,這也是穩定性的一種體現。大多數系統都做不到在某部分出現問題時隔離缺陷帶來的連鎖反應。試想一下,在自己做過的項目中把Common Logging(或slf4j)的包拿掉,系統能只損失日誌功能而其他部分正常運作嗎?但是對於基於OSGi架構開發系統,在設計時自然會考慮到模塊自治和動態化,當某部分不可用時如何處理是每時每刻都會考慮的問題,如果軟件在開發階段跟隨着OSGi的設計原則來進行,自然而然會實現強魯棒性的系統。

   OSGi環境下可以做到動態修復缺陷。許多系統都有停機限制,要求7×24小時運行,對於這類系統,OSGi的動態化能力在出現問題時就非常有用,可以做到不停機地增加或禁止某項功能、更新某個模塊,甚至建立一個統一更新的模塊倉庫,讓系統在不中斷運行的情況下做到自動更新升級。

   前面提出的兩個問題可以總結爲OSGi是否能提升開發效率和軟件質量。OSGi在這兩方面的作用與軟件設計得是否合理關係非常密切,這時OSGi好比一個針對“設計”這個因素的放大槓桿,配合好的設計它會更加穩定、高效,而遇到壞的設計,反而會帶來更多問題。


3問:OSGi能讓系統運行得更快嗎?

   系統引入OSGi的目的可能有很多種,但一般不包括解決性能問題。如果硬要說OSGi對性能有什麼好處,大概就是讓那些有“系統潔癖”的用戶可以組裝出爲自己定製的系統了。例如GlassFish v3.0服務器是基於OSGi架構的,它由200多個模塊構成,如果不需要EJBJMS這類功能,就可以把對應的模塊移除掉,以獲得一個更精簡的服務器,節省一些內存。總體上講,OSGi框架對系統性能是有一定損耗的,我們從執行和內存兩方面來討論。

   首先,OSGi是在Java虛擬機之上實現的,它沒有要求虛擬機的支持,完全通過Java代碼實現模塊化,在執行上不可避免地會有一些損耗。例如,OSGi類加載的層次比普通Java應用要深很多,這意味着需要經過更多次的類加載委派才能找到所需的類。在兩個互相依賴的模塊間發生調用時,可能會由於類加載器互相鎖定而產生死鎖;要避免死鎖的出現,有時候不得不選用有性能損失的串行化的加載策略。在服務層上,動態性(表現爲服務可能隨時不可用)決定了應用不能緩存服務對象,必須在每次使用前查找,這種對OSGi服務註冊表的頻繁訪問也會帶來一些開銷。使用一些具體的OSGi服務,例如使用HTTP Service與直接部署在Web容器中的Servlet相比會由於請求的橋接和轉發產生一些性能損耗。

   其次,從內存用量來看,OSGi允許不同版本的Package同時存在,這是個優點,但是客觀上會佔用更多內存。例如,一個庫可能需要 ASM 3.0,而同一應用程序使用的另一個庫可能需要ASM 2.0,要解決這種問題,通常需要更改代碼,而在OSGi中只需要付出一點Java方法區的內存即可解決。不過,如果對OSGi動態性使用不當,可能會因爲不正確持有某個過期模塊(被更新或卸載的模塊)中一個類的實例,導致該類的類加載器無法被回收,進而導致該類加載器下所有類都無法被GC回收掉。

   僅從性能角度來說,OSGi確實會讓系統性能略微下降,但是這完全在可接受範圍之內。使用OSGi開發時應該考慮到性能的影響,但不應當將其作爲是否採用OSGi架構的主要決策依據。


4問:OSGi能支撐企業級開發嗎?

   不管關於“OSGi是否能支撐企業級開發”的討論結果如何,一個必須正視的事實是OSGi對企業級開發的支撐能力正在迅速增強。從2007OSGi聯盟建立企業專家組以來,OSGi的發展方向已經逐漸調整到企業級應用領域。在IBMApacheEclipse基金會等公司和組織推動下,企業級OSGi正在變得越來越成熟。

   在企業級OSGi出現之前,企業級開發要麼是走Java EE的重量級路線,要麼是走SSH的輕量級路線。企業級OSGi被引入後並沒有扮演一個“革命者”的角色,沒有把Java EESSH中積累的東西推倒重來,OSGi更像是在扮演一個“組織者”的角色,把各種企業級技術變爲它的模塊和服務,使以前的企業級開發技術在OSGi中依然能夠發揮作用。

   OSGi企業級規範中定義了JDBCJPAJMXJTAJNDI等各種Java EE技術以及SCASDO這些非Java EE標準的企業級技術在OSGi環境中的應用方式,這些容器級的服務都可以映射爲OSGi容器內部的服務來使用。並且到現在,企業級規範定義的內容已經不僅停留在規範文字中,已經有不少專注於OSGi企業級服務實現框架出現(例如Apache Aries)了。

   另一方面,OSGiBlueprint容器規範統一了Java大型程序中幾乎都會用到的依賴注入(DI)方式,使基於BlueprintOSGi模塊可以在不同的DI框架中無縫遷移。這個規範得到ApacheSpringSource等組織的大力支持,目前這些組織已經發布了若干個Blueprint規範的實現容器(例如Apache GeronimoEquinox VirgoVirgo前身就是SpringSource捐獻的Spring DM 2.0)。在最近兩三年時間裏,企業級OSGi成爲Java社區技術發展的主要方向之一,其發展局面可以說是如火如荼。

   不過,我們在使用企業級OSGi的時候也要意識到它還很年輕,其中很多先進的思想可能是遺留程序根本沒有考慮過的,還有不少問題的解決都依賴於設計約束來實現。因此,如果是遺留系統的遷移,或者設計本來就做得不好,那麼使用OSGi會遇到不少麻煩。以最常見的數據訪問爲例,如果以前遺留系統使用了ORM方式訪問數據庫,而遷移到OSGi時沒有把實體類統一抽取到一個模塊,那麼ORM模塊的依賴就很難配置了,這時不得不使用Equinox Buddy甚至DynamicImport-Package這類很不優雅的方式來解決。另一個問題是集羣,OSGi擁有支持分佈式的遠程服務規範,而OSGi的動態性是針對單Java虛擬機實例而言的,因此要在集羣環境下保持OSGi的動態性,就必須自己做一些工作才行。

作者周志明,資深Java技術專家,對JavaEE企業級應用開發、OSGiJava虛擬機和工作流等都有深入的研究,並積累了豐富的經驗。著有暢銷書《深入理解Java虛擬機:JVM高級特性與最佳實踐》。現任遠光軟件股份有限公司平臺開發部經理兼平臺架構師,先後參與過國家電網、南方電網等多個國家級大型ERP項目的平臺架構工作。

作者謝小明,資深Java技術專家,具有豐富的JavaEE企業級應用開發開發經驗,對OSGiSpringHibernate等技術和框架有比較深入的研究和認識。現任遠光軟件股份有限公司平臺開發部平臺架構師,先後參加與過國家電網、南方電網等多個國家級大型ERP項目的平臺架構工作。

本文節選自《深入理解OSGi:Equinox原理、應用與最佳實踐》,周志明、謝小明著,由機械工業出版社出版。


轉自:http://www.programmer.com.cn/15563/


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