OSGi模塊化框架是很早就出來的一個(gè)插件化框架再来,最早Eclipse用它而出名,但這些年也沒有大熱雖然OSGi已經(jīng)發(fā)布了 版本1到版本5∥偶現(xiàn)在用的最多的茵乱,也是本文講述基于的是Equinox的OSGi實(shí)現(xiàn),同時(shí)也是Eclipse核心孟岛,Spring Dynamic Module也是基于Equinox瓶竭。OSGi框架為java系統(tǒng)提供了一個(gè)通用的容器,該系統(tǒng)中的 bundle渠羞,無需停止系統(tǒng)斤贰,即可實(shí)現(xiàn) bundle 的安裝、卸載次询。OSGi是Java中目前唯一的一個(gè)模塊化荧恍、動(dòng)態(tài)化的規(guī)范。在模塊化方面OSGi聯(lián)盟已經(jīng)研究了很多年了屯吊,因此OSGi規(guī)范對于模塊的物理隔離送巡、模塊的交互、多版本這些方面都有了非常完善的機(jī)制盒卸,并且也得到了現(xiàn)在幾乎所有的App Server廠商或開源社區(qū)的認(rèn)可授艰,但至今沒有被JAVA納入語言級(有待觀察)。OSGi的突出特點(diǎn)有:
可以動(dòng)態(tài)加載世落、更新和卸載模塊而不用停止服務(wù)
實(shí)現(xiàn)系統(tǒng)的模塊化、版本化糟需,允許多版本bundule同時(shí)服務(wù)
Service model允許模塊/插件相互依賴但松耦合屉佳,分享服務(wù)更簡單
OSGi運(yùn)行在JVM之上,其架構(gòu)圖如下圖所示:
OSGi適用場景
很多人錯(cuò)誤的使用了OSGi, 套用了OSGi架構(gòu)把系統(tǒng)復(fù)雜化洲押。在我看來武花,OSGi的用處在于“模塊化”和“熱插拔”。模塊化包括模塊化杈帐、版本化和面向服務(wù)的設(shè)計(jì)体箕。熱插拔也就是說模塊/bundle的熱插拔,它可以實(shí)現(xiàn)更新和升級模塊/bundle(即系統(tǒng)的一部分)而無需重啟整個(gè)系統(tǒng)挑童。
如果你的系統(tǒng)套用了OSGi架構(gòu)累铅,bundle的相互依賴關(guān)系復(fù)雜,又沒有bundle動(dòng)態(tài)加載站叼、動(dòng)態(tài)更新娃兽、動(dòng)態(tài)卸載和動(dòng)態(tài)監(jiān)聽的機(jī)制,都是靜態(tài)啟動(dòng)所有bundle尽楔,那就是為了OSGi架構(gòu)而OSGi架構(gòu)投储,把問題復(fù)雜化了第练。其代價(jià)也是很大的,因?yàn)樵瓉砟愕膉ar包用maven來處理依賴關(guān)系和自動(dòng)更新也很方便玛荞,而由于整個(gè)系統(tǒng)建立在OSGi規(guī)范上娇掏,你的應(yīng)用所依賴的其他組件也“不得不”遷移到OSGI上來,再加上OSGI獨(dú)特的ClassLoader設(shè)計(jì)勋眯,使bundle間的類互相訪問受到一定的約束婴梧,一切都需要遷移到OSGi的約束上來。
舉個(gè)例子來說凡恍,就像Eclipse提供了動(dòng)態(tài)加載志秃、更新和刪除插件的機(jī)制,因?yàn)樗锩嬗幸粋€(gè)插件注冊和反注冊的接口和插件加載嚼酝、更新和刪除的監(jiān)聽線程浮还,這樣允許你動(dòng)態(tài)加載、更新和刪除Eclipse插件而無需重啟Eclipse闽巩。當(dāng)然钧舌,如果你當(dāng)前進(jìn)程調(diào)用了某插件,比如js語法高亮涎跨,而某插件更新了洼冻,那么當(dāng)前的js實(shí)例還是需要重新打開的。但整個(gè)Eclispe無需重啟隅很。
Java模塊化的難點(diǎn)
OSGi的一個(gè)重要特性就是模塊化撞牢,OSGi提供了一套模塊化的體系,這其中則會有明確的模塊之間接口暴露以及依賴的定義叔营,因此能夠更好的實(shí)現(xiàn)高內(nèi)聚和低耦合屋彪。那么,Java模塊化難點(diǎn)在哪绒尊?模塊的實(shí)現(xiàn)和傳統(tǒng)的編程方法確實(shí)有一些差別畜挥,主要體現(xiàn)在模塊之間類訪問的隔離、版本選擇這兩個(gè)方面婴谱。如希望更好的設(shè)計(jì)模塊化的系統(tǒng)蟹但,開發(fā)者需要掌握ClassLoader機(jī)制、模塊之間類的交互方法(這包括了模塊怎么樣對外提供可訪問的package谭羔、怎么樣訪問其他模塊提供的package华糖、如何選擇適合版本的package等)胃夏。如果不懂以上這些菊卷,貿(mào)然套用OSGi框架會誤入歧途骚腥。
重要概念:Bundle
Bundle — A bundle is a JAR file with special OSGi entries in its manifest and containing classes, resources, and other JARs登疗。Bundle谆扎,可以將其理解為自描述的 JAR 文件项郊。Bundle在OSGi中是部署的最小單位偏化,因此嗅绰,可以把它理解為模塊。在 bundle 的 manifest 文件中向族,會有對本 bundle 的標(biāo)識呵燕、提供的功能 (Export-package) 及依賴性 (Import-Package/Require-Bundle) 的定義。每個(gè) bundle 在運(yùn)行時(shí)自己的類加載器 (Class Loader)件相,這樣可以做到一方面把不同的 bundle 里面的類區(qū)別開來再扭,當(dāng) bundle 被卸載時(shí),只有這個(gè) bundle 的類加載器中的信息會丟失夜矗;另一方面泛范,可以在自己的 bundle 內(nèi)部充分利用 Java 的成員訪問控制機(jī)制。
Bundle通過MANIFEST.MF進(jìn)行自描述紊撕,下面是一個(gè)例子:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Popup Plug-in
Bundle-SymbolicName: com.example.myosgi; singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: com.example.myosgi.Activator
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle類隔離機(jī)制
每個(gè)Bundle均為獨(dú)立的ClassLoader罢荡,是java動(dòng)態(tài)化實(shí)現(xiàn)的基礎(chǔ)。默認(rèn)情況下有Boostrap classLoader (jre/lib/classes)对扶、Extension classloader (jre/lib/ext)区赵、 System classloader (classpath指定),應(yīng)用可以自行實(shí)現(xiàn)classloader及動(dòng)態(tài)的加載類浪南,或加載特定目錄下的類笼才。
Bundle的生命周期
Lifecycle — A lifecycle is the sequence of states a bundle goes through: uninstalled, installed, resolved, starting, stopping, active. 生命周期圖如下所示:
要注意的是:bundle狀態(tài)變?yōu)镽esolved并不表示能提供服務(wù),所以啟動(dòng)所有的bundle不表示類都已經(jīng)加載到內(nèi)存了络凿。Resolve bundle做以下的幾件事情:尋找bundle依賴的包是否存在以及被resolve骡送,尋找匹配的import package,required bundle絮记,如尋找則進(jìn)入檢查摔踱,檢查沒有沖突就形成綁定關(guān)系,以便加載類的時(shí)候能直接加載(但僅僅Resolved到千,不代表類被加載了)。在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流群赴穗。交流學(xué)習(xí)群號:948368769里面會分享一些資深架構(gòu)師錄制的視頻錄像:有Spring憔四,MyBatis,Netty源碼分析般眉,高并發(fā)了赵、高性能、分布式甸赃、微服務(wù)架構(gòu)的原理柿汛,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源络断,目前受益良多如果你的BundleActivationPolicy是LAZY惰性加載裁替,bundle.loadClass()調(diào)用才會到達(dá)Active狀態(tài)。如果你的bundle的MANIFEST.MF中配置的Bundle-activator存在貌笨,那就調(diào)用其start方法弱判,從starting進(jìn)入active狀態(tài)。
osgi> ss
"Framework is launched."
id State Bundle
15STARTING com.example.serviceconsumer_1.0.0.X
16RESOLVED com.example.serviceprovider_1.0.0.X
---------------------
下面的圖更詳細(xì)的解釋了這一點(diǎn):
OSGi Service
Service — A service is an object instance exposed under the one or more interfaces that it implements and a map of properties. 簡單來說锥惋,Service model允許每個(gè)bundle對外分享一組服務(wù)昌腰,其它的bundle都可以調(diào)用這些接口的服務(wù)。這也就是OSGi bundle之間調(diào)用的方式膀跌。Service可以用來:
Export functionality from a bundle to other bundles
Import functionality from other bundles
Register listeners for events from other bundles
Expose external devices, such as UPnP devices or even hardware, to other OSGi bundles. See theDeviceandUPnPAPIs
Expose java code running in OSGI to an external network, e.g. via the UPnP orSOAPprotocols.
Bundle configuration, using theConfiguration Manager
實(shí)際做法來看遭商,通常會把接口和實(shí)現(xiàn)分開。接口放到一個(gè)bundle里面捅伤。實(shí)現(xiàn)(service)放到另外一個(gè)bundle里面劫流,類似下面的圖示中,bundle A和B是Service暑认,其interface放到Bundle C:
也可以是提供一個(gè)jar包困介,里面定義了擴(kuò)展接口,然后規(guī)定新的擴(kuò)展bundle必須實(shí)現(xiàn)該jar包里面定義的interface蘸际。實(shí)現(xiàn)示意圖如下所示(OsgiCommand接口定義在擴(kuò)展點(diǎn)jar包里面座哩,新的bundle必須包含):
Bundle的Service之間交換方式和注冊方式:
通過bundleContext.registerService注冊服務(wù),然后通過bundleContext.getServiceReference獲取服務(wù)(不推薦)
使用監(jiān)聽器listeners
ServiceListener和ServiceTracker提供bundle和service的動(dòng)態(tài)監(jiān)聽粮彤,ServiceTracker可以動(dòng)態(tài)監(jiān)聽未來的bundle和service(OSGi Release 2提供的ServiceTracker根穷,一般推薦)
通過Declarative Service(OSGiDS,或者Spring Dynamic Module(DM))的方式(OSGi Release 4開始导坟,重點(diǎn)推薦S炝肌)
第二種通過ServiceTracker來查詢或偵聽服務(wù)注冊和注銷的例子代碼:
package com.ibm.osg.example.mygetservice;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.ibm.osg.example.mtservice.MyTestService;
public class MyBundleActivator
implements BundleActivator, Runnable
{
private boolean done=false;
private ServiceTracker testServiceTracker;
// Bundle Activator Start Method
public void start(BundleContext context)
{
/* Here we initialize and open our ServiceTracker.
It will track any service registering under
the "com.ibm.osg.example.mtservice.MyTestService"
interface.
*/
testServiceTracker =
new ServiceTracker(context,
"com.ibm.osg.example.mtservice.MyTestService",
null);
testServiceTracker.open();
// Here we start a thread that will continue
// to use our service until
// the bundle is stopped.
Thread t = new Thread(this);
t.setName("mygetservice thread");
t.start();
}
/*Bundle Activator Stop Method -- here we stop
the thread and close the
ServiceTracker*/
public void stop(BundleContext context)
{
done=true;
testServiceTracker.close();
}
//Here is a method that uses the service
//we are tracking. First we get
//the service
//from the tracker, then we call its printMessage
//method.
public void useService(String message){
MyTestService testService = (MyTestService)
testServiceTracker.getService();
if( testService != null )
{
// If the service is available then use it.
testService.printMessage(message);
}
else{
// If the service is not available then perform an acceptable action.
// Here we just print the message to standard out and indicate the service
// was not available.
System.out.println("No MyTestService available - " + message);
}
}
// Simply continues to use the test service
// every second until the done flag is set.
public void run(){
int i = 0;
done = false;
while (!done) {
useService("message from test " + i++);
try{
Thread.sleep(1000);
}
catch( InterruptedException ie ){
}
}
}
}
OSGi簡單起步
從Eclipse創(chuàng)建OSGi的bundle是非常簡單的
Slideshare: OSGi理論與實(shí)戰(zhàn)
OSGi動(dòng)態(tài)加載刪除bundle
使用監(jiān)聽器listeners
ServiceListener和ServiceTracker提供bundle和service的動(dòng)態(tài)監(jiān)聽,ServiceTracker可以動(dòng)態(tài)監(jiān)聽未來的bundle和service(OSGi Release 2提供的ServiceTracker惫周,一般推薦)在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流群尘惧。交流學(xué)習(xí)群號:948368769里面會分享一些資深架構(gòu)師錄制的視頻錄像:有Spring,MyBatis递递,Netty源碼分析喷橙,高并發(fā)、高性能登舞、分布式贰逾、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化菠秒、分布式架構(gòu)等這些成為架構(gòu)師必備的知識體系疙剑。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多
通過Declarative Service(OSGiDS,或者Spring Dynamic Module(DM))的方式(OSGi Release 4開始言缤,重點(diǎn)推薦=朗础)
具體實(shí)現(xiàn):
對于DS,<Define a declarative OSGi Service>, <IBM: Declaring your services to OSGi Declarative Services>轧简,<java OSGI Declarative Services Component bundles Example>驰坊。
對于Spring DM,<OSGI and Spring Dynamic Modules – Simple Hello World>, <Hello, OSGi, Part 2: Introduction to Spring Dynamic Modules>
分布式OSGi(Distributed OSGi)
OSGi容器可以包含幾千個(gè)bundlue沒有問題哮独,但如何應(yīng)對幾十萬個(gè)的情況拳芙?如何像EJB3一樣具有分布式部署和便攜性呢?有一個(gè)OSGi的子項(xiàng)目:分布式OSGi(Distributed OSGi)皮璧。
上圖是一個(gè)demo演示舟扎,兩個(gè)分布式OSGi Container,都部署了Greeter接口bundle悴务,都基于分布式OSGi實(shí)現(xiàn)睹限,能實(shí)現(xiàn)分布式調(diào)用OSGi Service。分布式OSGi(Distributed OSGi)還可以與RESTful Service (JAX-RS / JSR-339)整合讯檐。分布式OSGi有幾十萬個(gè)bundle怎么管理羡疗,這是個(gè)麻煩,而且如何啟動(dòng)停止别洪,啟動(dòng)順序怎么樣叨恨?可管理性是個(gè)麻煩,ACE項(xiàng)目試圖解決這個(gè)事情挖垛。
DOSGi的原理(由Distribution provider來給OSGi Service創(chuàng)建Endpoint痒钝,使這些Service在OSGi Container外部可訪問,另外一端則創(chuàng)建代理痢毒;此外送矩,有監(jiān)聽器來偵聽OSGi Service的創(chuàng)建和啟停等):
分布式OSGi與ZooKeeper
ZooKeeper是Hadoop的一個(gè)子項(xiàng)目,它是一個(gè)針對大型分布式系統(tǒng)的可靠協(xié)調(diào)系統(tǒng)哪替,提供的功能包括:配置維護(hù)栋荸、名字服務(wù)、分布式同步凭舶、組服務(wù)等晌块。ZooKeeper的目標(biāo)就是封裝好復(fù)雜易出錯(cuò)的關(guān)鍵服務(wù),將簡單易用的接口和性能高效库快、功能穩(wěn)定的系統(tǒng)提供給用戶摸袁。
分布式OSGi(Distributed OSGi)在Discovery這一塊使用了ZooKeeper钥顽。所謂Discovery模塊就是用來發(fā)現(xiàn)和偵聽分布式的遠(yuǎn)端的可用的Endpoints义屏。Discovery Ditributed with Zookeeper的架構(gòu)圖:
參考:DOSGi使用ZooKeeper server的安裝和Demo