我的OSGI学习历程

       系统开发到了一定程度的时候,我发现以往有很多可以再优化的地方,于是试图去优化,最终虽然优化了,但是因为牵连的东西有点多,所以花了不少时间。经过一番思考,才意识到需要优化的不是那些代码,而是那些架构。我们需要的大系统应该由多个相对独立的小系统组成,将业务分而治之。于是就去找合适的方法来达到这个目的,很幸运的是,我发现了OSGI。

       刚开始, 就只是知道 OSGI是一个实现模块化的架构,而且最初是作为一个嵌入式的java应用架构规范发展起来的,网上传得最有名的就是在BMW上的车载系统和Eclipse IDE这个两个应用。BMW没钱买来了解,只好看看Eclipse吧 

       想当年,我也尝试用eclipse来开发java程序,但嫌eclipse用起来太繁杂,用了几天,就没兴趣了,就转用比较简单,容易上手的Netbeans来做开发的IDE(也正是为了和其他习惯用eclipse的同事能合作好点,所以也就用上Maven,这是题外话了)。回过头来看Eclipse,依然是感觉超多的Plugins,弄的眼花缭乱。以前一直觉得Eclipse本身的开发也太复杂了吧,这么多的功能,得要多强大的全局观念才能兼顾得过来呀。知道有OSGI这回事后,才理解到,原来Eclipse并不需要多强的全局观念,它的功能都被“分而治之”了,这不正是我们的系统所追求的架构吗?于是,豁然开朗,但是这么多的令人眼花缭乱的功能还是令我静不下心来了解eclipse。

      于是,继续找,后来在apache上找到了felix,感觉挺清爽的,并且在felix.apache.org上了解到了很多OSGI方面的一些知识,再后来就看到了karaf,有了karaf,准备运行时环境就轻松了,于是就开始尝试去开发OSGI的bundle,然后放到karaf上跑。

      刚开始尝试做OSGI bundle,首先意识到的是如何组建一个bundle工程,想到既然一直以来都是用Maven做项目工程的,还是继续用Maven吧,再说OSGI Bundle在打包方面和JAR唯一的区别就是manifest.mf而已,而manifest.mf也是jar本来就有的东西,在OSGI bundle来说,只是内容上多加了几个小项而已。于是继续沿用原来的一套,拿个maven-jar-plugin插件在pom.xml里维护manifest项。

      接着就是尝试编码,直接写个BundleActivator的实现了,实现start和stop方法,只是往里面写个System.out.println Balabala...。mvn package生成一个bundle。扔到<karaf-root>/deploy文件夹下,运行<karaf-root>/bin/karaf.bat或<karaf-root>/bin/karaf.sh,console出现Balabala......。尝试着stop/start bundle,看着不断Balabala...。

     之后,就尝试一个bundle A在manifest.mf里export package,然后另一个bundle B在manifest.mf里import package,然后写代码用bundle A中的类创建个实例,并调用方法。扔karaf里运行之,还正常。尝试不同次序地stop/start这两个bundle,

     接着就尝试在Activator的start方法里,用BundleContext的registerService发布一个OSGI Service。也正常,在karaf console里用命令ls <bundle-id>,可以列出发布出来的服务。

    再接着尝试在另一个bundle里的Activator的start方法里,用BundleContext的getServiceReference方法引用刚才那个OSGI Service。也正常,可以引用并调用服务方法。

    So far so good!

   尝试不同次序地stop/start这两个bundle,问题出现了:服务引用那里会有返回null的情况,噩梦开始了。
  
   由于bundle启动的次序不确定,发布service的bundle可能会比引用service的bundle启动得晚。就会出现引用的服务为null。

   刚开始,我是通过在引用service的bundle的manifest里,添加import package来依赖发布service的bundle,这样当发布者未active时,引用者就不会resolved,所以,就曲线式决定了bundle的启动先后。但这是个坏实践。

  后来就采用ServiceListener或ServiceTracker的方式来引用服务,这样,服务就不再依赖bundle的启动次序了。但还有个致命的缺点----要写大量的代码。

  于是开始找DI的解决方案,刚开始选择了Declarative Service(DS),在felix项目下,有Service Component Runtime(SCR)的bundle可以支持Declarative Service,在manifest里用Service-Component项指定一个DS的XML文档,这个文档描述了服务及其组装关系。当bundle启动后,SCR bundle就读取该XML文档,解析并按文档组装起应用,如果某些服务未可用的话,就会延后到服务可用时再完成组装。这个过程完全由SCR bundle来管理。我们就不需要写ServiceListener或ServiceTracker来组装应用了,节省了大量的精力。

   再后来,又发现了更好的Blueprint的DI方案,于是就转向用Blueprint来取代Declarative Service,实现应用的组装。

  到此为止,系统还只是在单一个平台上运作,而我做的系统偏偏是由几个系统来组成的分布式的系统,于是继续找分布式的解决方案,于是又尝试ROSGI(这个开源项目已没有继续维护更新了)、接着CXF DOSGI,CXF DOSGI是可用,但发布的singlebundle版本太大(什么依赖都集成进去了),multiplebundle版本要剪裁,还存在一些和我自己用的某些bundle的版本冲突问题(这个也令我意识到import和export package时指定版本号及bundle版本管理的重要性,以后有空再聊聊这个问题)。所以cxf dosgi用了一段时间后,决定采用消息中间件来负责分布式系统间通讯的架构。

   在cxf dosgi里,osgi service可以跨framework地互相调用,但服务对应的API bundle就需要在关联的系统中各运行一个。采用消息中间件后,就不需要在不同的系统间共享API了,消息所在的队列、主题及消息本身的消息头,甚至消息体都隐式地定义了API,再加上消息的异步和同步的机制,使消息中间件的应用进一步增强了每个子系统的内聚,使子系统间的耦合更加松散。

   很“巧合”的是,Apache有个Servicemix的顶级开源项目,这个Servicemix和karaf的渊源十分深厚----karaf就是servicemix项目的内核独立分离出来的一个开源项目,所以,将所有以前开发的bundle移植到servicemix上,不费吹灰之力。Servicemix集成了ActiveMQ,于是Servicemix就成了我用来实现用消息中间件取代cxf dosgi方式的首选。

   上了Servicemix,自然就会用上了它集成的Camel,Camel强大的企业集成能力,将系统提升到面向服务的ESB层面了。

    既然上了SOA的贼船,就要想想将业务的规则和流程提炼出来,借助规则引擎或工作流引擎来实现,这是一条分支,暂时放下,日后再提。

    继续OSGI方面,在camel的支持下,无状态的服务纷纷被提炼出来,成为全系统通用的服务,而这些服务可以由众多细粒度的osgi服务来组装而成。服务采用消息处理器的方式实现,在对外的接口方面,则借助ESB来实现,要什么接口,就用什么Camel Component,这些Component都是现成的。至于消息路由,还是用那个用惯了的Blueprint吧:在Camel的命名空间(http://camel.apache.org/schema/blueprint)直接在CamelContext节点中定义消息路由。

    无状态的服务好解决,那么业务逻辑相关的有状态的部分怎么办呢?除了刚才提到的用规则引擎和工作流引擎外,Camel的消息处理器可以负责这部分的工作,具体的细节怎么做?我还要学习学习。

    讲了这么多,似乎还没有涉及到企业应用方面的东西,例如:持久化、事务(包括分布式事务)、Web等等。但还是暂时打住,分支太多了,这条裹脚(国脚)布也够长了。

猜你喜欢

转载自killko.iteye.com/blog/1788713