OSGi模块化框架是很早就出来的一个插件化框架,最先Eclipse用它而出名,但这些年也没有大热虽然OSGi已经发布了 版本1到版本5。如今用的最多的,也是本文讲述基于的是Equinox的OSGi实现,同时也是Eclipse核心,Spring Dynamic Module也是基于Equinox。OSGi框架为java系统提供了一个通用的容器,该系统中的 bundle,无需中止系统,便可实现 bundle 的安装、卸载。OSGi是Java中目前惟一的一个模块化、动态化的规范。在模块化方面OSGi联盟已经研究了不少年了,所以OSGi规范对于模块的物理隔离、模块的交互、多版本这些方面都有了很是完善的机制,而且也获得了如今几乎全部的App Server厂商或开源社区的承认,但至今没有被JAVA归入语言级(有待观察)。OSGi的突出特色有:html
能够动态加载、更新和卸载模块而不用中止服务java
实现系统的模块化、版本化,容许多版本bundule同时服务apache
Service model容许模块/插件相互依赖但松耦合,分享服务更简单编程
OSGi运行在JVM之上,其架构图以下图所示:性能优化
不少人错误的使用了OSGi, 套用了OSGi架构把系统复杂化。在我看来,OSGi的用处在于“模块化”和“热插拔”。模块化包括模块化、版本化和面向服务的设计。热插拔也就是说模块/bundle的热插拔,它能够实现更新和升级模块/bundle(即系统的一部分)而无需重启整个系统。架构
若是你的系统套用了OSGi架构,bundle的相互依赖关系复杂,又没有bundle动态加载、动态更新、动态卸载和动态监听的机制,都是静态启动全部bundle,那就是为了OSGi架构而OSGi架构,把问题复杂化了。其代价也是很大的,由于原来你的jar包用maven来处理依赖关系和自动更新也很方便,而因为整个系统创建在OSGi规范上,你的应用所依赖的其余组件也“不得不”迁移到OSGI上来,再加上OSGI独特的ClassLoader设计,使bundle间的类互相访问受到必定的约束,一切都须要迁移到OSGi的约束上来。并发
举个例子来讲,就像Eclipse提供了动态加载、更新和删除插件的机制,由于它里面有一个插件注册和反注册的接口和插件加载、更新和删除的监听线程,这样容许你动态加载、更新和删除Eclipse插件而无需重启Eclipse。固然,若是你当前进程调用了某插件,好比js语法高亮,而某插件更新了,那么当前的js实例仍是须要从新打开的。但整个Eclispe无需重启。框架
OSGi的一个重要特性就是模块化,OSGi提供了一套模块化的体系,这其中则会有明确的模块之间接口暴露以及依赖的定义,所以可以更好的实现高内聚和低耦合。那么,Java模块化难点在哪?模块的实现和传统的编程方法确实有一些差异,主要体如今模块之间类访问的隔离、版本选择这两个方面。如但愿更好的设计模块化的系统,开发者须要掌握ClassLoader机制、模块之间类的交互方法(这包括了模块怎么样对外提供可访问的package、怎么样访问其余模块提供的package、如何选择适合版本的package等)。若是不懂以上这些,贸然套用OSGi框架会误入歧途。eclipse
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 的标识、提供的功能 (Export-package) 及依赖性 (Import-Package/Require-Bundle) 的定义。每一个 bundle 在运行时本身的类加载器 (Class Loader),这样能够作到一方面把不一样的 bundle 里面的类区别开来,当 bundle 被卸载时,只有这个 bundle 的类加载器中的信息会丢失;另外一方面,能够在本身的 bundle 内部充分利用 Java 的成员访问控制机制。maven
Bundle经过MANIFEST.MF进行自描述,下面是一个例子:
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均为独立的ClassLoader,是java动态化实现的基础。默认状况下有Boostrap classLoader (jre/lib/classes)、Extension classloader (jre/lib/ext)、 System classloader (classpath指定),应用能够自行实现classloader及动态的加载类,或加载特定目录下的类。
Lifecycle — A lifecycle is the sequence of states a bundle goes through: uninstalled, installed, resolved, starting, stopping, active. 生命周期图以下所示:
要注意的是:bundle状态变为Resolved并不表示能提供服务,因此启动全部的bundle不表示类都已经加载到内存了。Resolve bundle作如下的几件事情:寻找bundle依赖的包是否存在以及被resolve,寻找匹配的import package,required bundle,如寻找则进入检查,检查没有冲突就造成绑定关系,以便加载类的时候能直接加载(但仅仅Resolved,不表明类被加载了)。在此我向你们推荐一个架构学习交流群。交流学习群号:948368769里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多若是你的BundleActivationPolicy是LAZY惰性加载,bundle.loadClass()调用才会到达Active状态。若是你的bundle的MANIFEST.MF中配置的Bundle-activator存在,那就调用其start方法,从starting进入active状态。
osgi> ss "Framework is launched." id State Bundle 15STARTING com.example.serviceconsumer_1.0.0.X 16RESOLVED com.example.serviceprovider_1.0.0.X --------------------- |
下面的图更详细的解释了这一点:
Service — A service is an object instance exposed under the one or more interfaces that it implements and a map of properties. 简单来讲,Service model容许每一个bundle对外分享一组服务,其它的bundle均可以调用这些接口的服务。这也就是OSGi bundle之间调用的方式。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
实际作法来看,一般会把接口和实现分开。接口放到一个bundle里面。实现(service)放到另一个bundle里面,相似下面的图示中,bundle A和B是Service,其interface放到Bundle C:
也能够是提供一个jar包,里面定义了扩展接口,而后规定新的扩展bundle必须实现该jar包里面定义的interface。实现示意图以下所示(OsgiCommand接口定义在扩展点jar包里面,新的bundle必须包含):
Bundle的Service之间交换方式和注册方式:
经过bundleContext.registerService注册服务,而后经过bundleContext.getServiceReference获取服务(不推荐)
ServiceListener和ServiceTracker提供bundle和service的动态监听,ServiceTracker能够动态监听将来的bundle和service(OSGi Release 2提供的ServiceTracker,通常推荐)
经过Declarative Service(OSGiDS,或者Spring Dynamic Module(DM))的方式(OSGi Release 4开始,重点推荐!)
第二种经过ServiceTracker来查询或侦听服务注册和注销的例子代码:
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 ){ } } } } |
从Eclipse建立OSGi的bundle是很是简单的
Slideshare: OSGi理论与实战
使用监听器listeners
ServiceListener和ServiceTracker提供bundle和service的动态监听,ServiceTracker能够动态监听将来的bundle和service(OSGi Release 2提供的ServiceTracker,通常推荐)在此我向你们推荐一个架构学习交流群。交流学习群号:948368769里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多
经过Declarative Service(OSGiDS,或者Spring Dynamic Module(DM))的方式(OSGi Release 4开始,重点推荐!)
具体实现:
对于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容器能够包含几千个bundlue没有问题,但如何应对几十万个的状况?如何像EJB3同样具备分布式部署和便携性呢?有一个OSGi的子项目:分布式OSGi(Distributed OSGi)。
上图是一个demo演示,两个分布式OSGi Container,都部署了Greeter接口bundle,都基于分布式OSGi实现,能实现分布式调用OSGi Service。分布式OSGi(Distributed OSGi)还能够与RESTful Service (JAX-RS / JSR-339)整合。分布式OSGi有几十万个bundle怎么管理,这是个麻烦,并且如何启动中止,启动顺序怎么样?可管理性是个麻烦,ACE项目试图解决这个事情。
DOSGi的原理(由Distribution provider来给OSGi Service建立Endpoint,使这些Service在OSGi Container外部可访问,另一端则建立代理;此外,有监听器来侦听OSGi Service的建立和启停等):
ZooKeeper是Hadoop的一个子项目,它是一个针对大型分布式系统的可靠协调系统,提供的功能包括:配置维护、名字服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
分布式OSGi(Distributed OSGi)在Discovery这一块使用了ZooKeeper。所谓Discovery模块就是用来发现和侦听分布式的远端的可用的Endpoints。Discovery Ditributed with Zookeeper的架构图:
参考:DOSGi使用ZooKeeper server的安装和Demo