下载本文 PDF 版本 PDF

OSGi 如何改变了我的生活

乐高假设的承诺尚未完全实现,但它们仍然是值得追求的目标。

彼得·克里斯,AQUTE

在 20 世纪 80 年代早期,我发现了 OOP(面向对象编程),并深深地爱上了它。像往常一样,这种爱意味着说服管理层投资这项新技术,最重要的是,送我去参加酷炫的会议。所以我向我的经理推销了这项技术。我向他描绘了美好的未来,有一天我们将从现成的类创建应用程序。我们将从存储库中获取这些类,将它们放在一起,瞧,一个新的应用程序就诞生了。

今天,我们或多或少地认为对象是理所当然的,但如果我诚实地说,我在 1985 年向我的经理做的推销从未真正实现。对象的重用从未达到布拉德·考克斯和他的软件 IC 模型等人的预期水平,包括我自己。尽管如此,这个乐高假设仍然是值得追求的圣杯。

我们需要的

在 90 年代后期,当我在爱立信研究院工作时,我有机会进一步探索乐高假设。我被要求参与家庭自动化软件标准化流程。这项工作由一群公司发起,包括 IBM、爱立信、北电、Sybase、Sun、法国电信、摩托罗拉和飞利浦,后来这些公司变成了 OSGi 联盟,这是一个推广开放动态组件平台的非营利组织。

我们在 1999 年必须解决的关键问题是如何让不同供应商的应用程序在家庭嵌入式计算机中可靠地协同工作并共享资源。听起来很简单,但一个家庭可以包含许多连接许多设备的异构网络。从一开始就很清楚,我们需要一个模型,应用程序可以(重新)使用彼此的资源和功能。

强迫每个应用程序都包含其驱动程序和库的模型是行不通的(例如,今天 Java 手机软件的工作方式);封闭的 API 集(在 Java 中称为配置文件)也不行,因为目标市场的多样性太大了。更重要的是,我们希望这些应用程序在彼此之间没有先验知识的情况下进行协作。我们还想要热插拔性。必须关闭家庭服务器才能添加新功能的想法不是很吸引人。请记住,在 1999 年 OSGi 联盟构思之时,Windows 95 是主流,我们技术小组的所有人都厌倦了每次配置更改后都重启 PC。“无需重启”成为我们早期的口头禅。

另一个口头禅是,OSGi 联盟的每个成员公司都可以用自己的方式实施规范——不仅在大的方面,也在小的方面。也就是说,不同的部分可能来自不同的供应商,而来自不同供应商的这些部分必须能够协作。

在这样的环境中,安全性不是可选项。因此,从一开始就很清楚,安全性必须内置于架构中。从不同来源运行具有不同信任级别的应用程序的能力是一个关键要求,尽管如果家庭服务器在完全受控的环境中管理,也应该可以实现非安全系统。我们必须支持完全封闭的系统、围墙花园和完全开放的系统。

家庭服务器的环境是我们必须做出的第一个决定。这并不太难,因为 Java 真的没有竞争对手。以这种面向对象语言闻名的 Sun 微系统公司是 OSGi 成员。因此,Java 或多或少是既定的,没有任何其他参与者的反对。

Java 最好的部分是 VM(虚拟机)架构。这使得在从小型到大型的许多不同设备中轻松支持规范成为可能——这绝对是必须的。今天,VM 允许我们从各种各样的语言(PHP、Scala、Groovy、Ruby、Python 等)中进行选择,并在更广泛的硬件上运行。

对象纠缠

那时,对象显然很有用,但由于对象纠缠问题,它们的重用受到限制。大多数软件从业人员都知道,当他们使用类 A 时,它会拖入库 B,而库 B 又需要库 C,依此类推,永无止境,他们会感到绝望。在 OOP 的早期,我们对封装如此热衷,以至于我们大多数人从未真正关注耦合。有趣的是,OO 的前身是结构化编程,它的口头禅是:低耦合,高内聚。我想我们太忙于继承、多态和其他面向对象的新奇事物了,所以谁想向这些老古董学习呢?然而,在 1998 年,很明显,家庭自动化应用程序的 Java 框架需要比类更大的粒度的东西。

我们需要的是一个组件框架,您可以在其中以稳健可靠的方式动态管理组件。我们希望能够安装新组件、更新现有组件、启动/停止组件和卸载它们——所有这些都无需重启,完全动态。如果组件 A 使用组件 B,则必须管理此依赖项,而不是在组件启动时通过异常发现。

如果您有组件,那么这些组件需要相互查找。更棘手的是,由于我们不允许重启,组件还必须检测到它们的依赖项何时消失。为了解决这些动态问题,我们开发了一个服务模型。组件应该能够注册一个服务对象,以便另一个组件可以找到此服务并绑定到它,以及在服务消失时解除绑定。每个服务都必须有一个指定其行为的合同。这种架构本来可以被称为 SOA(面向服务的架构),早在这个术语流行之前。然而,与当今大多数流行的 SOA 相比,OSGi 模型不需要 Web 服务或任何其他类型的通信层。OSGi 服务是一个 POJO(普通旧 Java 对象)。

OSGi 规范

Sun 不仅提供了环境,还为我们提供了一个名为 JES(Java 嵌入式服务器)的产品。JES 允许在运行的 Java 虚拟机中安装和卸载捆绑包(ZIP/JAR 文件)。也就是说,VM 在许多不同的应用程序之间共享。捆绑包或多或少是我们正在寻找的组件。然而,作为优秀的开发人员,我们觉得我们必须对其进行更改,所以我们这样做了——进行了广泛的更改。我们花了大约八年时间,而且时间还在流逝。

现在的 OSGi 规范,版本 4,绝对成熟。尽管家庭自动化市场在 2000 年随着互联网炒作而崩溃(恰好在版本 1 发布时),但 OSGi 规范已被电话、汽车和工业自动化行业采用,并且最近在企业计算领域变得非常流行,OSGi 技术旨在增强或取代较重的 J2EE 容器和其他应用程序服务器。

示例系统:Idéfix

工业自动化是我最喜欢的 OSGi 技术领域。我就是喜欢嵌入式编程和 Java。不幸的是,消费电子公司非常关注 BOM(物料清单),并且往往忽略了 C 和 C++ 等语言中较高的软件开发成本。这通常意味着没有 Java,或者在最好的情况下,是阉割版的 Java(供鉴赏家使用的 CLDC),因为 VM 许可成本以及所需的额外 CPU 和内存。这个问题在工业自动化市场或多或少是不存在的,因为较低的销量使软件成本更加关键。

举例来说,假设一家公司生产一种工业测量设备,名为 Idéfix,售价约为 50 万欧元。(这是一个假设的设备,但信息是从实际项目中收集的,由于保密协议,这些项目无法发布。)Idéfix 有大量硬件扩展,用于为其提供来自传输线的样本,正确定位样本,注入特殊液体,处理废弃物,并执行一系列需要极高精度的复杂测量。

显然,这种设备的销量不大。大多数销售都需要进行广泛的定制,以使设备适应现有环境。这些设备的软件架构必须非常灵活,并且专注于降低进行(和维护)调整的成本。

虽然在这个市场中,调整对于达成销售是必要的,但 Idéfix 的另一个关键驱动因素是远程管理。世界贸易已经爆炸式增长,像 Idéfix 这样的设备遍布世界各地,有时甚至在不适宜居住的地方使用。销量低使得很难有足够靠近客户的技术人员在出现问题时进行访问。这使得可靠性变得很重要,因为机器会发生故障。因此,执行远程诊断和软件更新的能力至关重要。

Idéfix 是在过去几年中与一家咨询公司合作开发的,该公司提出了使用 Java 和 OSGi 规范作为基础的想法。OSGi 规范与 Idéfix 等设备的要求非常匹配。这些规范提供了一个动态组件模型,简化了标准客户调整、可扩展硬件架构、远程管理和无数复杂测量算法的难题。当您需要某种形式的插件性时,这非常有用。而今天谁不需要呢?

虽然 Java 可以用于设备的实时方面,但 Idéfix 架构将硬实时代码与主应用程序代码分离开来。这些硬实时方面由辅助处理器提供,有时嵌入在硬件扩展本身中,有时通过插件卡提供。将硬实时任务与主控制软件分离的原因是为了使大部分软件不那么复杂和关键。这种选择将 Java VM 置于控制之下,但不是以实时敏感的方式,从而简化了整体设计。

加载设备驱动程序

Idéfix 的设计人员使用 1-wire 设备来识别扩展。这是一种简单的协议,可以在与电源线相同的电线上运行,允许非常便宜和简单的(微型)1-wire 设备网络通过地线和电源/协议线连接。每个 1-wire 设备都有一个唯一的 64 位地址,可以通过 1-wire 协议发现。一旦扩展正确连接,1-wire 设备中的唯一代码就可以用于识别已插入的扩展类型。这实际上是 OSGi 设备访问规范设计的一个示例。此规范很好地阐明了 OSGi 动态服务模型。让我们详细了解一下它是如何工作的。

1-wire 总线需要一个控制器来检测网络上是否存在 1-wire 设备。在 OSGi 系统中,此控制器通过基本驱动程序进行编程和管理,基本驱动程序本身就是一个捆绑包。当基本驱动程序检测到新的 1-wire 设备时,它会向 OSGi 服务注册表注册一个服务,该服务代表检测到的 1-wire 设备。OSGi 服务是一个 POJO,以及一组属性,这些属性提供有关服务的信息。在本例中,一个属性描述设备类别,即 1-wire(其他类型可以是 USB、Firewire、RFID、以太网、ESB 等)。另一个属性是唯一 ID,在本例中是嵌入在所有 1-wire 芯片中的 1-wire 唯一代码。

设备访问子系统侦听具有这些特定设备属性的服务的注册,并且当发布此类服务时,它将收到来自 OSGi 框架的通知。它将使用这些属性通过一个或多个驱动程序定位器服务查询捆绑包数据库,这些服务可以使用简单的文件查找、本地数据库查找或通过查询远程管理系统来实现,以满足需求。设备定位器服务返回匹配的驱动程序位置列表 (URL)。如果设备访问子系统尚未加载这些捆绑包,则会将其加载到框架中。

这些驱动程序捆绑包中的每一个在启动时都会注册一个驱动程序服务。此驱动程序服务用于评估驱动程序的适用性;然后由驱动程序选择器服务选择最佳驱动程序。随后,此获胜驱动程序用于附加设备服务。然后,驱动程序通常会通过注册一个反映已连接设备实际功能的新服务来改进此设备服务。该过程如图 1 所示,其中圆圈是捆绑包,三角形是服务。注册是连接到三角形(服务)的尖端,获取是连接到直边,侦听是连接到斜边。这些动作按照序号发生。

例如,具有以下 1-wire ID 的设备

  67.4B.A7.CD.90.AA.1A.01 

因此可以精细化为一个直径为 5 厘米的样品盘。如果 1-wire 基本驱动程序检测到设备已卸下,则会立即取消注册相应的设备服务。设备访问捆绑包将收到有关取消注册的通知,并将导致取消注册样品盘服务。

此示例显示了这些捆绑包的解耦程度。1-wire 接收器基本驱动程序不知道标准化的设备访问捆绑包,也不知道样品盘服务。设备访问捆绑包知道设备属性,但不知道 1-wire 控制器的基本驱动程序的任何线索,也不知道有关样品盘的任何具体信息。所有这三个组件都知道的是来来往往的服务。对于样品盘,如果 1-wire 控制器基本驱动程序捆绑包因软件更新而停止,或者样品盘被移除,则没有区别。这两个事件的处理方式相同。这种通过服务注册表进行解耦的模式是关键的 OSGi 设计模式,它有许多应用。它在故障和/或环境动态变化的情况下提供了一个健壮的模型,传统环境只能通过复杂和专门的代码来处理这种情况。

安装设备驱动程序捆绑包听起来很简单,但在实践中,OSGi 规范的大部分内容都被模块层所占据,模块层负责管理 VM 中的捆绑包。关键问题是这些捆绑包共享类和资源。也就是说,一个捆绑包可以从另一个捆绑包导入类。正如我们幼儿园时首次发现的那样,共享很难!在计算环境中,当事物不仅来来往往时,共享就更加困难。

Java 的模块化

捆绑包之间共享类是通过 OSGi 元数据处理的,该元数据包含在捆绑包的 ZIP 文件中的清单中。清单是标头名称和关联值的列表。要了解共享的工作原理,您首先需要了解 Java 包。Java 包是一个包含相关类和资源的目录。类在不同的包中可以具有相同的名称。在 OSGi 系统中,包被导出到其他捆绑包、从其他捆绑包导入或完全对其他捆绑包隐藏。每个导出的包都有一个版本;导入可以在一系列版本上进行。能够对其他捆绑包隐藏包的优点是,可以修改这些包中的类以用于未来的版本,而不会影响任何客户端代码。这与普通的 Java 根本不同,在普通的 Java 中,没有导入、没有导出、没有隐藏类,也没有任何指导解析过程的版本。

在 OSGi 系统中,每个捆绑包都有自己的类空间,这是一个一致的类集。一致性意味着具有相同名称的类是相同的,并且无法访问不一致的类。然而,不同的捆绑包可以属于不同的类空间,从而允许同一应用程序或库的不同版本同时驻留在同一 VM 中。OSGi 框架自动将捆绑包分隔到不同的类空间中,以便它们不会冲突。

在管理类空间的同时解析导入到导出的问题是一个非常困难的问题,在每种可能的情况下都不能保证有解决方案。解析算法是 OSGi 规范中实现者具有显着自由度的少数几个领域之一,以允许以不同方式进行优化的创新解决方案。幸运的是,捆绑包开发人员完全免受这个非常困难的问题的影响。所有这些复杂性都隐藏在一个易于使用的 API 后面。因此,在我们的示例中,驱动程序捆绑包只需要提供一个 URL,OSGi 框架可以从中找到适当的捆绑包;然后捆绑包可以驻留在文件系统、Web 服务器、数据库或任何具有 Java URL 的位置。

捆绑包是 ZIP 文件,因此可以包含不仅仅是 Java 代码的内容。将其与定义明确的生命周期相结合,该生命周期是自省的,并在重要时刻生成事件,您将拥有一个系统,可以将 Java 代码以外的内容交付到设备。例如,需要在 Idéfix 中的辅助处理器上运行的代码。安装此代码的任务可以留给设备驱动程序,但对于所有不同的设备来说,这将是非常相似的代码。此外,出于可靠性考虑,如果另一个捆绑包管理所有辅助处理器的代码,则会更好。因此,Idéfix 设备有一个捆绑包,当它被安装时,它会在捆绑包中的特定目录中查找(生命周期事件的所有通知都会发送出去)。如果此目录包含辅助处理器的代码映像,则此捆绑包将自动安装它。如果捆绑包被更新,则辅助处理器上的映像将被更新。如果捆绑包被卸载,则代码将直接从辅助处理器中删除。将捆绑包的(受管理)生命周期与其他事物匹配非常流行,以至于它有了自己的名称:扩展器。扩展器用于注册帮助资源、安装数据库表、提供 Web 服务等等。

面向服务的商业模式

制造复杂产品的行业的明显趋势是从销售产品转向专注于销售服务——即真实的服务或功能。例如,GE 停止向航空公司销售发动机(产品),而是改为向他们销售推力(服务)。另一个例子是海德堡,一家生产印刷机(产品)的公司,但现在按印刷的份数(服务)获得报酬。商业服务模式使客户更敏捷,因为他们需要较少的不同技能,并且它将责任和专业知识放在制造商手中,从而可以进行更好的优化。

Idéfix 产品旨在用于基于服务的商业环境中。显然,具有远程管理的捆绑包模型是一个很好的匹配,因为这些设备最终将遍布世界各地。然而,这种商业服务模式要取得成功,一个强烈的要求是安装在客户场所的设备是安全且防篡改的。如果客户可以重置计数器或以未经授权的方式使用设备,制造商将蒙受损失。因此,安全模型至关重要。

OSGi 安全模型从捆绑包开始。可以使用公钥/私钥方案对捆绑包进行签名,以使用 X.509 证书进行身份验证。尽管我们必须设置一些额外的规则,但这基本上是 Java 提供的模型。然而,Java 安全模型中的一个关键问题是配置管理,因为该模型具有如此精细的粒度。尝试管理 Java 安全性非常困难,以至于许多系统最终只有两个静态级别:无安全性或所有权限。

OSGi 通过提供基于捆绑包的清晰模型简化了安全管理。每个捆绑包都有一组权限,可以使用条件权限管理服务动态更改这些权限。此外,每个捆绑包都可以包含一个权限文件,该文件进一步限制捆绑包从环境获得的权限。其想法是,签名者可以在签名之前验证该捆绑包的安全范围。这种模型简化了安全处理,尽管安全权限的管理仍然是一个复杂的问题。

OSGi 和开源

使 Idéfix 易于销售给软件开发人员的另一个优势是 Eclipse,一个免费的开源开发环境。Eclipse 的开发人员最初创建了自己的插件模型,但在 2003 年,他们正在寻找更动态的模型。在评估了几个模块化系统之后,OSGi 技术脱颖而出(好吧,我作为团队的一员可能有所帮助)。然后,Eclipse 团队出色地完成了开胸手术,移除了旧的插件引擎,用与 OSGi 兼容的全新框架替换了它,并创建了一个兼容层,使所有现有的插件都与新系统兼容。最重要的是,Eclipse 提供了一个开发捆绑包的优秀环境。尽管这不是关键要求,但如果可以在与桌面相同的环境中开发和测试设备的捆绑包,则肯定会产生协同作用。这是 OSGi 的一个关键优势,也是我们有时称其为通用中间件的原因。正确设计的捆绑包几乎可以在任何地方运行。

Idéfix 的架构允许大量使用开源项目。对于 OSGi 框架,Idéfix 选择了 Apache Felix,它是关键的开源 OSGi 实现之一。它还使用了来自 Knopflerfish(一个具有商业支持的开源项目)和 Equinox(Eclipse OSGi 实现)的捆绑包。这些项目实现了 OSGi 版本 4 的大部分服务规范。Idéfix 也可以从 Makewave、ProSyst、IBM 等公司提供的商业实现中选择一个。

不幸的是,并非所有基于 Java 的开源项目都为 OSGi 框架提供了正确的元数据,即使当使用 make、ant、maven 或 bnd 工具构建项目时,必要的标头也很容易添加。当在没有 OSGi 的情况下使用 JAR(Java 存档文件)时,这些标头不会导致任何开销或问题。我们希望 OSGi 在我们行业中的采用率不断提高将在明年帮助显着改善这种情况。同时,Eclipse 和 Apache Felix 中有两个项目为许多开源软件项目提供了捆绑化的 JAR。

应用程序编程模型

过去十年伟大的软件见解之一是 POJO 的概念。POJO 风格的编程意味着我们编写与 Java 环境耦合但不与特定容器耦合的 Java 代码。我们已经痛苦地了解到,将我们的应用程序逻辑耦合到框架是一个坏主意,因为它会锁定我们。然而,我们总是需要一些代码来桥接我们的容器和 POJO。此代码将任何依赖项注入到 POJO 中,以便它可以忽略这些依赖项的来源。尽管 OSGi 框架很轻量级,但它也是某种容器,因此也适用相同的规则。

已经开发了几个基于依赖注入的应用程序框架,以允许在 OSGi 环境中进行 POJO 编程。这些框架隐藏了动态依赖项的复杂性,透明地添加了事务和远程处理等额外层,并简化了应用程序逻辑的编程。

OSGi 联盟为版本 4 服务平台指定了声明式服务应用程序模型。声明式服务模型是一个基于 OSGi 服务模型的小而有效的依赖注入模型。Apache Felix 使用 iPOJO 采取了一种有趣的方法。此模型通过分析字节码并推断许多常见任务的意图来减少许多编程工作。

OSGi 最著名的应用程序编程模型是 Spring。Spring 的守护者 SpringSource 扩展了 Spring bean 配置模型以连接到动态 OSGi 服务。事实证明,这非常有效,因为 Spring 和 OSGi 的重叠很少,并且非常互补。Spring-OSGi 目前在企业软件市场非常受欢迎。在不久的将来,SCA(服务组件架构),一个来自 OASIS 的努力,汇集了企业软件市场的所有主要参与者,将把 SCA 映射到 OSGi,这是一个很有希望的组合。

五种不同的编程模型,并且还在增加,对于 OSGi 这样的技术来说已经很多了,因为这些模型之间的协作可能会受到严重损害。然而,所有这些应用程序模型都以 OSGi 服务注册表为基础。通过注册表,Spring-OSGi 应用程序可以与 iPOJO 应用程序或声明式服务应用程序无缝协作。因此,开发人员可以选择哪种模型为他们提供最佳解决方案,但仍然可以重用以其他应用程序模型编写的组件。

反思

OSGi 技术的未来看起来非常光明。它是一个成熟的标准,在许多市场中得到验证,有许多实现,并且已证明可以很好地扩展。如果我们可以向更广泛的受众解释这项技术,它将为真正的通用中间件标准奠定基础,这可能会对我们的行业产生巨大影响。

我们已经完成了基础工作,但我们远未完成。我们需要更多公司愿意将标准朝着不同的方向发展,以便我们可以更接近实现乐高假设的承诺:一个将组件连接在一起以创建高级应用程序的模型。在那一天,我终于可以说我对经理的最初推销已经实现了。

彼得·克里斯是 OSGi 技术总监和 aQute 的 CEO。他曾担任众多国际公司的顾问,介绍面向对象技术。1994 年,他移居瑞典为爱立信工作。2001 年,他被 OSGi 兼职聘用,担任其技术总监。他的职责包括编辑规范并担任 OSGi 的布道者。

acmqueue

最初发表于 Queue vol. 6, no. 1
数字图书馆 中评论本文





更多相关文章

萨特南·辛格 - 使用容器对容器进行集群级日志记录
本文介绍了如何使用开源工具实现集群级日志记录基础设施,并使用与组合和管理正在记录的软件系统相同的抽象来部署该基础设施。收集和分析日志信息是运行生产系统以确保其可靠性并提供重要审计信息的必要方面。已经开发了许多工具来帮助聚合和收集特定服务器(例如,Fluentd 和 Logstash)上运行的特定软件组件(例如,Apache Web 服务器)的日志。


伦·武内 - ASP:集成挑战
使用 ASP 和向 ASP 提供增值产品的第三方供应商的组织需要与它们集成。ASP 通过提供基于 Web 服务的 API 来实现这种集成。通过 Internet 与 ASP 集成和与本地应用程序集成之间存在显着差异。与 ASP 集成时,用户必须考虑许多问题,包括延迟、不可用性、升级、性能、负载限制和缺乏事务支持。


克里斯·理查森 - 解开企业 Java
关注点分离是计算机科学中最古老的概念之一。该术语由 Dijkstra 在 1974 年创造。1 它很重要,因为它简化了软件,使其更易于开发和维护。关注点分离通常通过将应用程序分解为组件来实现。然而,存在跨领域关注点,它跨越(或横切)多个组件。这些类型的关注点无法通过传统的模块化形式来处理,并且会使应用程序更加复杂和难以维护。


米奇·亨宁 - CORBA 的兴衰
根据人们开始计数的确切时间,CORBA 大约有 10-15 年的历史。在其生命周期中,CORBA 从早期采用者的前沿技术,发展成为流行的中间件,再到相对默默无闻的小众技术。值得研究一下为什么 CORBA——尽管曾经被誉为“电子商务的下一代技术”——遭受了这种命运。CORBA 的历史是计算行业多次见证的历史,并且似乎当前的中间件努力,特别是 Web 服务,将重演类似的历史。





© 保留所有权利。

© . All rights reserved.