《深入理解OSGi:Equinox原理、应用与最佳实践》一1.2 为什么使用OSGi

简介: 本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第1章,第1.2节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.2 为什么使用OSGi

没有什么技术是万能的,任何一门技术都有它的适用场景和最佳实践方法。OSGi不只是一门技术,更多的是一种做系统架构的工具和方法论,如果在不适用的场景中使用OSGi,或者在适用的场景中不恰当地使用OSGi,都会使整个系统产生架构级的缺陷。因此,了解什么时候该用OSGi是与学会如何使用OSGi同样重要的事情。
每个系统遇到的业务环境都是不一样的,笔者不希望以经验式的陈述去回答“什么时候该用OSGi”或 “为什么要使用OSGi”这样的问题,而试图通过几个问题的讨论和利弊权衡,让读者自己去思考为什么这些场景适用OSGi。如果读者是第一次通过本书接触OSGi,那么可能对某些讨论的内容会感到困惑,笔者建议尽可能在阅读完全书或者在准备真正在项目中使用OSGi的时候,再回过头读一遍本节的内容。

1.2.1 OSGi能让软件开发变得更容易吗

不可否认,OSGi的入门门槛在Java众多技术中算是比较高的,相对陡峭的学习曲线会为第一次使用OSGi开发系统的开发人员带来额外的复杂度。
OSGi规范由数十个子规范组成,包含了上千个不同用途的API接口。OSGi规范显得这样庞杂的主要原因是实现“模块化”本身需要解决的问题就非常多。模块化并不仅仅是把系统拆分成不同的块而已—这是JAR包就能做的事情,真正的模块化必须考虑到模块中类的导出、隐藏、依赖、版本管理、生命周期变化和模块间交互等一系列的问题。
鉴于OSGi本身就具有较高的复杂度,“引入OSGi就能让软件开发变得更容易”无论如何是说不通的,小型系统使用OSGi可能导致开发成本更高。但是这句话又不是完全错误的,随着系统不断发展,在代码量和开发人员都达到一定规模之后,OSGi带来的额外成本就不是主要的关注点了,这时候的主要矛盾是软件规模扩大与复杂度随之膨胀间的矛盾。如图1-3所示,代码量越大、涉及人员越多的系统,软件复杂度就会越高,两者成正比关系。这个观点从宏观角度看是正确的,具体到某个系统,良好的架构和设计可以有效减缓这个比率。基于OSGi架构的效率优势在这时候才能体现出来:模块化推动架构师设计出能在一定范围内自治的代码,可以使开发人员只了解当前模块的知识就能高效编码,也有利于代码出现问题时隔断连锁反应。OSGi的依赖描述和约束能力,强制开发人员必须遵循架构约束,这些让开发人员“不自由”的限制,在系统规模变大后会成为开发效率的强大推动力。

screenshot

可以用一个更具体的场景来论述上面的观点,解析OSGi架构如何在开发效率上发挥优势。有经验的架构师会有这样的感受:设计一个具有“自约束能力”的系统架构非常不容易。最常见的情况是设计人员设想得很美好,开发人员在实现时做出来的产品却不是那样。大部分软件公司是通过“开发过程”、“编码规范”、“测试驱动”,甚至“人员熟练度”来保证开发人员实现的代码符合设计人员的意图。这样即使在开发阶段做到符合设计需求,也很难保证日后维护人员能够继续贯彻原有的设计思想;随着开发的时间越来越长,系统最终实现的样子可能和原有的设计产生越来越大的偏差。在软件工程中,将这种现象称为“架构腐化”。架构的“自约束能力”就是指限定不同开发人员在实现功能的时候,实现方式都是一致的,最好只有唯一一条遵循设计意愿的路可走,别的方法无法达到目的。更通俗地说就是,尽可能使程序员不写出烂代码。
举个最浅显的例子,如果有开发人员在Web层中使用DAO直接操作数据库,或者在DAL层直接从HttpSession对象中取上下文信息,这样的代码也许能逃过测试人员的黑盒测试,但是显然是不符合软件开发基本理论的。前者可能因绕过Service层中的事务配置而出现数据安全问题;后者限制了这样的DAO就只能从Web访问,无法重用和进行单元测试。如果项目中出现这样的代码,笔者认为首要责任在架构师,因为架构师没有把各层的依赖分清,如果Web层只依赖Service层的JAR包,那么程序员就无法访问到DAO,如果DAL层没有依赖Servlet API的JAR包,那么程序员就不可能访问HttpSession对象,这就是一种架构缺乏自约束能力的表现。
大概没有哪个架构师会犯上面例子那样幼稚的错误。但是,实际情况也远比例子中的复杂,甚至有一些问题是Java语言本身的缺陷带来的,例如,依赖了一个JAR包就意味着能够访问这个JAR包中的一切类和资源,因为JAR包中的内容没有Public、Private和Protected之分,无法限制用户能访问什么、不能访问什么。更复杂的情况是在引入了同一个JAR包的不同版本时怎么办?如果依赖包需要动态变化怎么办?使用OSGi一个很重要的目的就是弥补Java中资源精细划分的缺陷,加强架构的自约束能力。
虽然OSGi起源于精小软件占多数的嵌入式领域,但是在Java SE/EE领域中,对于越庞大的系统,使用OSGi进行模块化拆分就越能发挥出优势。在商业上已经有一些使用OSGi控制软件复杂度增长、延缓架构腐化速度的成功案例,如Eclipse Marketplace,它已经拥有了上千个插件,插件的开发者来自全球各地,技术水平差异很大,插件实现的功能也各不相同,是OSGi让这些插件基本遵循了统一的架构约束,并且一般不会因为某个插件的缺陷影响整个Eclipse的质量。

1.2.2 OSGi能让系统变得更稳定吗

笔者遇到过许多由OSGi框架引发的问题,例如,最典型的ClassNotFoundException异常、类加载器死锁或者在动态环境下的OutOfMemoryError问题等,这些都是基于OSGi架构开发软件时很常见的。从这一方面看,使用OSGi确实会增加系统不稳定的风险,所以,在开发过程中团队中有一两个深入了解OSGi的成员是必要的。
不过,软件是否稳定不是只看开发阶段可能出现多少异常就能衡量的,软件的“稳定”应是多方面共同作用的结果。除了关注开发阶段是否稳定之外,还要关注是否能积累重用稳定的代码,问题出现时能否隔断连锁反应蔓延,缺陷是否容易修复等。在这些方面,OSGi就可以带来相当多的好处,例如:
OSGi会引导程序员开发出可积累可重用的软件。我们无法要求程序刚开发出来就是完全稳定的,但可以在开发过程中尽可能重用已稳定的代码来提升程序质量。大家知道,写日志可以使用Log4j,做ORM会引入Hibernate,Java中有许多经过长期实践检验的、被证实为稳定的开源项目,这些开源项目的共同特征是都经过良好的设计,能够很方便地在其他项目中使用。相对而言,在自己开发项目时很多人没有注意到要进行可积累的设计。一种典型现象是项目中出现一些“万能的包”,通常名字会是XXXCommons.jar、XXXUtils.jar等,这些包中存放了在项目中被多次调用的代码,但是这样的包不能叫做可重用包。当这些包越来越大、类越来越多、功能越来越强时,与这个项目的耦合就越紧密,一般也就无法用在其他项目中了。在OSGi环境下,“大杂烩”形式的模块是很难生存的,如果某个模块有非常多的依赖项,那么没有人愿意为了使用其中少量功能去承担这些间接依赖的代价。因此设计者必须把模块设计得粒度合理,精心挑选对外发布的接口和引入的依赖,把每个模块视为一个商业产品来对待,这样才能积累出可重用的模块,也利于提高程序稳定性。
基于OSGi比较容易实现强鲁棒性的系统。普通汽车坏掉一个轮胎就会抛锚,但是飞机在飞行过程中即使坏了其中一个引擎,一般都还能保持正常飞行。对于软件系统来说,如果某一个模块出了问题,能够不波及其他功能的运作,这也是稳定性的一种体现。大多数系统都做不到在某部分出现问题时隔离缺陷带来的连锁反应。试想一下,在自己做过的项目中把Common Logging(或slf4j)的包拿掉,系统能只损失日志功能而其他部分正常运作吗?但是对于基于OSGi架构开发系统,在设计时自然会考虑到模块自治和动态化,当某部分不可用时如何处理是每时每刻都会考虑的问题,如果软件在开发阶段跟随着OSGi的设计原则来进行,自然而然会实现强鲁棒性的系统。
在OSGi环境下可以做到动态修复缺陷。许多系统都有停机限制,要求7×24小时运行,对于这类系统,OSGi的动态化能力在出现问题时就非常有用,可以做到不停机地增加或禁止某项功能、更新某个模块,甚至建立一个统一更新的模块仓库,让系统在不中断运行的情况下做到自动更新升级。
1.2.1节和1.2.2节提出的两个问题可以总结为OSGi是否能提升开发效率和软件质量。OSGi在这两方面的作用与软件设计得是否合理关系非常密切,这时OSGi好比一个针对“设计”这个因素的放大杠杆,配合好的设计它会更加稳定、高效,而遇到坏的设计,反而会带来更多问题。
1.2.3 OSGi能让系统运行得更快吗
系统引入OSGi的目的可能有很多种,但一般不包括解决性能问题。如果硬要说OSGi对性能有什么好处,大概就是让那些有“系统洁癖”的用户可以组装出为自己定制的系统了。例如GlassFish v3.0服务器是基于OSGi架构的,它由200多个模块构成,如果不需要EJB或JMS这类功能,就可以把对应的模块移除掉,以获得一个更精简的服务器,节省一些内存。总体上讲,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架构的主要决策依据。
1.2.4 OSGi能支撑企业级开发吗
不管关于“OSGi是否能支撑企业级开发”的讨论结果如何,一个必须正视的事实是OSGi对企业级开发的支撑能力正在迅速增强。从2007年OSGi联盟建立企业专家组以来,OSGi的发展方向已经逐渐调整到企业级应用领域。在IBM、Apache和Eclipse基金会等公司和组织推动下,企业级OSGi正在变得越来越成熟。
在企业级OSGi出现之前,企业级开发要么是走Java EE的重量级路线,要么是走SSH的轻量级路线。企业级OSGi被引入后并没有扮演一个“革命者”的角色,没有把Java EE或SSH中积累的东西推倒重来,OSGi更像是在扮演一个“组织者”的角色,把各种企业级技术变为它的模块和服务,使以前的企业级开发技术在OSGi中依然能够发挥作用。
OSGi企业级规范中定义了JDBC、JPA、JMX、JTA和JNDI等各种Java EE技术以及SCA、SDO这些非Java EE标准的企业级技术在OSGi环境中的应用方式,这些容器级的服务都可以映射为OSGi容器内部的服务来使用。并且到现在,企业级规范定义的内容已经不仅停留在规范文字中,已经有不少专注于OSGi企业级服务实现框架出现(例如Apache Aries)了。
另一方面,OSGi的Blueprint容器规范统一了Java大型程序中几乎都会用到的依赖注入(DI)方式,使基于Blueprint的OSGi模块可以在不同的DI框架中无缝迁移。这个规范得到Apache、SpringSource等组织的大力支持,目前这些组织已经发布了若干个Blueprint规范的实现容器(例如Apache Geronimo和Equinox Virgo,Virgo前身就是SpringSource捐献的Spring DM 2.0)。在最近两三年时间里,企业级OSGi成为Java社区技术发展的主要方向之一,其发展局面可以说是如火如荼。
不过,我们在使用企业级OSGi的时候也要意识到它还很年轻,其中很多先进的思想可能是遗留程序根本没有考虑过的,还有不少问题的解决都依赖于设计约束来实现。因此,如果是遗留系统的迁移,或者设计本来就做得不好,那么使用OSGi会遇到不少麻烦。以最常见的数据访问为例,如果以前遗留系统使用了ORM方式访问数据库,而迁移到OSGi时没有把实体类统一抽取到一个模块,那么ORM模块的依赖就很难配置了,这时不得不使用Equinox Buddy甚至DynamicImport-Package这类很不优雅的方式来解决。另一个问题是集群,OSGi拥有支持分布式的远程服务规范,而OSGi的动态性是针对单Java虚拟机实例而言的,因此要在集群环境下保持OSGi的动态性,就必须自己做一些工作才行。

相关文章
|
安全 Java Linux
OSGi 和 C++
2011年 9月我参加了OSGi社区在达姆施塔特的会议,并且有机会与其他与会者探讨本机c++实现的OSGi规范的现状。在这一事件之前我也一直想写一篇博客,来描述关于当前实现OSGi规范的现状和努力——类似于C / C++实现的OSGI框架。最后,这篇文章会给出OSGi本机实现的概述。
455 0
|
Java 程序员 数据中心
《深入理解OSGi:Equinox原理、应用与最佳实践》一第1章 Java模块化之路
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第1章,第1.1节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1437 0
|
Java 关系型数据库 应用服务中间件
《深入理解OSGi:Equinox原理、应用与最佳实践》一1.1 什么是OSGi
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第1章,第1.1节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1845 0
《深入理解OSGi:Equinox原理、应用与最佳实践》一3.7 本章小结
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第3章,第3.7节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1264 0
《深入理解OSGi:Equinox原理、应用与最佳实践》一1.3 本章小结
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第1章,第1.3节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1182 0
|
Java 应用服务中间件
《深入理解OSGi:Equinox原理、应用与最佳实践》一导读
笔者自2007年接触OSGi以来,曾在数个大型系统中使用过OSGi作为软件的基础架构,这一方面使笔者深刻感受到了OSGi带来的诸多好处以及OSGi设计思想的魅力;另一方面也使笔者注意到OSGi的入门门槛相对较高,如果没有足够的指导材料,开发人员从零开始学习并探索出OSGi的最佳实践需要很高的成本。
1394 0
|
Java API
《深入理解OSGi:Equinox原理、应用与最佳实践》一2.8 本章小结
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第2章,第2.8节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1121 0
《深入理解OSGi:Equinox原理、应用与最佳实践》一第二部分 OSGi规范与原理
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第二部分,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
911 0
|
Java 中间件 数据库连接
《深入理解OSGi:Equinox原理、应用与最佳实践》一2.5 OSGi的类加载架构
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第2章,第2.5节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1291 0
《深入理解OSGi:Equinox原理、应用与最佳实践》一2.7 本地化
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第2章,第2.7节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1004 0