• OSGi 对软件复杂度的影响


    出自 深度理解 osgi equinox 原理

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

    不可否认,OSGi 的入门门槛在 Java 众多技术中算是比较高的,相对陡峭的学习曲线会 为第一次使用 OSGi 开发系统的开发人员带来额外的复杂度。

    OSGi 规范由数十个子规范组成,包含了上千个不同用途的 API 接口。OSGi 规范显得这 样庞杂的主要原因是实现“模块化”本身需要解决的问题就非常多。模块化并不仅仅是把系 统拆分成不同的块而已— 这是 JAR 包就能做的事情,真正的模块化必须考虑到模块中类的 导出、隐藏、依赖、版本管理、生命周期变化和模块间交互等一系列的问题。

    鉴于 OSGi 本身就具有较高的复杂度,“引入 OSGi 就能让软件开发变得更容易”无论 如何是说不通的,小型系统使用 OSGi 可能导致开发成本更高。但是这句话又不是完全错误 的,随着系统不断发展,在代码量和开发人员都达到一定规模之后,OSGi 带来的额外成本 就不是主要的关注点了,这时候的主要矛盾是软件规模扩大与复杂度随之膨胀间的矛盾。如 图 1-3 所示,代码量越大、涉及人员越多的系统,软件复杂度就会越高,两者成正比关系。 这个观点从宏观角度看是正确的,具体到某个系统,良好的架构和设计可以有效减缓这个比 率。基于 OSGi 架构的效率优势在这时候才能体现出来 :模块化推动架构师设计出能在一定 范围内自治的代码,可以使开发人员只了解当前模块的知识就能高效编码,也有利于代码出 现问题时隔断连锁反应。OSGi 的依赖描述和约束能力,强制开发人员必须遵循架构约束, 这些让开发人员“不自由”的限制,在系统规模变大后会成为开发效率的强大推动力。 

    可以用一个更具体的场景来论述上面的观点,解析 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 对性能有什么好处,大概就是让那些有“系统洁癖”的用户可以组装出为自己定制的系统

    第 1 章 Java 模块化之路 15

    了。例如 GlassFish v3.0 服务器是基于 OSGi 架构的,它由 200 多个模块构成,如果不需要 EJB 或 JMS 这类功能,就可以把对应的模块移除掉,以获得一个更精简的服务器,节省一些 内存。总体上讲,OSGi 框架对系统性能是有一定损耗的,我们从执行和内存两方面来讨论。

    首先,OSGi 是在 Java 虚拟机之上实现的,它没有要求虚拟机的支持,完全通过 Java 代 码实现模块化,在执行上不可避免地会有一些损耗。例如,OSGi 类加载的层次比普通 Java 应用要深很多,这意味着需要经过更多次的类加载委派才能找到所需的类。在两个互相依赖 的模块间发生调用时,可能会由于类加载器互相锁定而产生死锁 ;要避免死锁的出现,有时 候不得不选用有性能损失的串行化的加载策略 。在服务层上,动态性(表现为服务可能随 时不可用)决定了应用不能缓存服务对象,必须在每次使用前查找,这种对 OSGi 服务注册 表的频繁访问也会带来一些开销。使用一些具体的 OSGi 服务,例如使用 HTTP Service 与直 接部署在 Web 容器中的 Servlet 相比会由于请求的桥接和转发产生一些性能损耗。1

    其次,从内存用量来看,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 技术以及

    例如 Equinox 中的 osgi.classloader.singleThreadLoads 机制。即使有了这个机制,非树状类加载架构下还 是有一些情况仅靠 OSGi 本身是没有办法解决的,需要等到 Java 7 中类加载器结构改进才能解决,可参 见:http://openjdk.java.net/projects/jdk7/features/#f352。

    16 第一部分 走近 OSGi

    SCA、SDO 这些非 Java EE 标准的企业级技术在 OSGi 环境中的应用方式,这些容器级的 服务都可以映射为 OSGi 容器内部的服务来使用。并且到现在,企业级规范定义的内容已经 不仅停留在规范文字中,已经有不少专注于 OSGi 企业级服务实现框架出现(例如 Apache Aries )了。1

    另一方面,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 的动态性,就必须自己做一些工作才行。 

  • 相关阅读:
    mysql学习笔记(七)
    Mysql学习笔记(八)
    vcpkg安装库
    nvm node版本管理工具
    Node.js版本管理工具nvm
    mysql安装后启动及navicat绿色版
    蚁景Web安全12期笔记
    python协程asyncio的个人理解
    EF或原生sql语句使用全文索引
    在拥挤和变化的世界中茁壮成长:C++ 2006–2020
  • 原文地址:https://www.cnblogs.com/viewcozy/p/4912926.html
Copyright © 2020-2023  润新知