软件组件:粗粒度与细粒度 |
级别: 中级 Michael Beisiegel, DE,IBM 软件组下属 Strategy and Technology 部门, IBM 2008 年 2 月 18 日 IBM® 中间件产品提供一系列软件组件技术,用于满足各种需求的应用程序。有些组件技术(如 JavaBeans)是细粒度的,而其它技术则是粗粒度的。本文将提供一组指导原则,用于在面向服务的体系结构(Service-Oriented Architecture,SOA)的上下文中对软件组件技术进行分类,对目前行业内占主导地位的各种组件技术进行定位。最终了解服务组件体系结构(Service Component Architecture,SCA)为何是最适合粗粒度组件的模型。<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES--> SOA 讨论的是,通过称为服务 的接口提供软件功能并支持称为面向服务 的业务概念,其中的应用程序作为一组服务构建,服务用户不知道服务如何以及在何处实现。除了接口(是 SOA 的主要部分,但不是良好的 SOA 的唯一内容)外,还有软件组件和组件模型。组件模型在从头创建新 SOA 服务时非常有用,但在从遗留 IT 资产创建 SOA 服务时也非常有用。您可以使用组件模型来提供遗留服务的抽象和对现有遗留资产进行再组合,特别是在组件模型支持语言中立性时尤为有用。 软件组件的概念涉及很多方面。本文的重点是在基于 SOA 的业务服务解决方案的上下文中使用的软件组件。在此上下文中,可以使用各种软件组件技术进行以下工作:
可以将软件组件概念分解为本文所称的粗粒度组件 和细粒度组件。不过,不可能精确地定义这两个概念。关于如何精确地确定组件粒度,行业中有有众多的不同看法。例如,在 OASIS SOA 参考模型(请参见参考资料部分提供的链接)中明确否决了需要考虑接口通信量的说法。相反,Wikipedia 定义(请参见参考资料)则主要关注组件间的数据交互频率和数量。而且,行业采用了耦合 的概念,此概念定义为两个软件组件间的关系所体现的脆弱性。术语松散耦合(脆弱性较小)和紧密耦合(脆弱性较大)经常被人当作粗粒度组件和细粒度组件的同义词误用。 本文将明确地说明耦合只是粒度的一个方面(如组件接口部分中所述),但同时也支持当前行业中的看法,即松散耦合组件通常是粗粒度的,而紧密耦合组件更可能是细粒度组件。值得注意的是,虽然有些组件模型将重点放在一种形式的粒度上,但其他的模型都在尝试在两个极端之间取得平衡。(有关如何基于本文所述的属性对组件模型进行归类,请参见组件技术目录部分。) 实际上,组件粒度会受到一系列因素的影响,如图 1 中所示。 图 1. 粒度连续体 插入连续体的每个属性都对组件的粒度有不同程度的影响。例如,图 1 中的关系图明确地指出了,抽象级别和数据表示形式对所评估的组件的粒度有很大的影响。本文稍后的部分将对每个属性进行更为详细的讨论。 您是计划构建粗粒度组件还是细粒度组件?选择正确的组件模型并不容易。目前行业中存在很多选择,每个选择都存在着重叠的功能,而每个选择都让人举棋不定。选择组件模型需要在 SOA 最佳实践和当前的 IT 技术上下文中了解业务问题的各个方面。本文提供了一组指导原则,可用于在 SOA 上下文中对软件组件技术进行归类,从而支持在 IBM 的中间件产品中最有效地使用这些技术。另外,文中还对当今行业中各种主导组件技术进行了定位。通过本文,您可以评估图 1 中所述的很多属性并获得其相关描述,并将看到一些有助于说明这些因素对整体粒度影响的示例。 本文讨论以下主要主题:
评估组件粒度时,将组件的各个功能方面划分为允许进行更为深入的分析的两个分支,将会很有帮助。不过,任何单个方面都不能用来明确地确定组件的粒度。相反,总是通过结合各个方面才能够确定粒度。本文在分析其中的每个方面时,将提供倾向于粒度连续体两端的总体指南。 组件的功能方面包括在采用某种编程语言、元数据描述或其他计算机可读格式中的组件的形状、结构和组织的具体认识。因为这样,在区分粒度时,需要关注组件接口和实现。 请注意,在尝试评估组件技术中的粒度时,实现语言的选择通常并不重要。 组件接口 通常描述为实体作为自己的外部视图提供的正式的抽象。为了对粒度进行总体区分,在此我们将讨论以下方面:
接下来让我们详细讨论其中的各个方面: 接口耦合:服务使用者和服务提供者之间的耦合程度由他们能够独立发展的程度决定。粗粒度组件体现的是松散耦合,而细粒度组件经常是紧密耦合的。例如,如果重新编译使用者和提供者导致交互出现问题,则两个组件之间存在紧密耦合关系。接口耦合的多个方面对独立性程度都有影响。这包括数据、版本控制、传输独立性、交互模式、对话和中介。 数据:通过文档交换数据可促进服务使用者和提供者间的松散耦合,因为文档是数据的独立于编程语言的规范化形式。相反,公开特定于语言的数据形式的组件接口可促进紧密耦合,因为这通常会强制在接口的每一端使用相同的编程语言。使用并不能方便地映射到文档的数据结构会更趋向于产生紧密耦合。面向对象的图表就是不能方便地映射到文档的数据结构的一个例子。分层数据结构可很好地映射为文档。而且,服务使用者和提供者之间交换的数据的质量也能指示组件粒度。粗粒度组件交换的消息比细粒度组件交换的消息更大。细粒度组件趋向于交换更小的消息,因为通常交互次数会更多些。 版本控制:服务提供者支持不要求接口的特定版本的客户机的能力可促进松散耦合。即,能够独立发展的使用者和提供者是松散耦合的,因此通常是粗粒度的。 传输独立性:具有不依赖于特定传输或协议机制的实现的服务可促进松散耦合。由于能够更改或向服务添加其他传输和协议端点,因此可以让服务供更多的使用者使用。 交互模式:采用文档样式接口设计的接口(请参见上面关于数据 的段落)并结合使用者和提供者之间较少量的交互就是松散耦合,因为服务可以更为方便地替换为其他服务,而不会对使用者造成影响。事实上,以文档为中心的数据模型通常意味着交互数量更少。这个属性组合明显地表明了应该采用粗粒度组件。细粒度组件将可能采用每个操作的数据量更少的接口(不是文档),因此通信次数更多(包括对接口使用 get 和 set 方法)。细粒度组件还趋向于在同一执行线程上交互,从而减少通信量较多的交互模式的开销。粗粒度组件模型通常包括对异步多线程交互的支持,这样可以使组件的临时耦合松散化。另请注意,延迟和可分布性的非功能方面也会与组件的交互模式有关。 对话:对话是一种特殊的交互模式形式,其中涉及的各方都知道之前交换的消息(即各方均保留状态信息)。这通常采用键或标识符的形式,用于使消息进行相互关联。承载运行时所执行的消息相关性操作从使用者或提供者的角度称为隐式相关性。隐式相关性可能会导致紧密耦合,因为这样可能会由于需要使用能实现隐式相关性的传输和协议而限制传输独立性。当组件接口包括能够使消息进行相互关联的数据时,就出现了显式相关性。因此,使用隐式相关性的组件接口与非对话接口的结构设计有所差异。显式相关性的接口在操作签名中包含额外的参数(相关器),或包括元数据来指示业务数据中提供相关器的位置,例如指向操作参数之一的 Xpath 表达式。如果相关器是基础设施数据,不应该与业务数据混杂在一起,则显式相关性可能会导致紧密耦合。最后,对话交互代表组件之间的临时耦合,但在细粒度组件和粗粒度组件中都有应用,因此并不能明显地指示粒度。 中介:如果能够在两个组件间通过中介传递接口,则意味着处理的是具有松散耦合关系的粗粒度组件。中介可以出现在客户端、服务端或两者之间的连接 上。各种中介点支持松散耦合服务通过服务总线进行代理。此功能需由粗粒度数据和交互模式提供支持。粗粒度服务可以连接到服务总线,以便通过中介进行传递,允许在请求者和提供者之间插入中间逻辑。这并不是细粒度组件模型中会出现的情况。在细粒度组件中,通常可以直接修改组件实现或配置,从而更为方便地通过中介传递两个组件间的交互。这样还可以减少执行时的开销。因此,在现有细粒度组件上使用中介模式的做法基本上没有用。插入服务质量语义的能力可以视为一种中介形式。组件模型的这个方面在组件的非功能特征部分进行了讨论。 动态性:引入对现有 IT 系统组合的更改所需的工作量是组件间耦合的一个功能。松散耦合服务更容易适应更改,从而可实现更为灵活的 IT 环境。紧密耦合服务趋向于在系统中带来连锁效应,因为一个更改可能会导致需要实现另一个更改。在引入更改时,还可能需要考虑操作语义。由于不支持版本控制或与旧的使用者不兼容而需要停止或重新启动服务,也表明这些服务是紧密耦合服务。 组件实现 通常描述为算法的实现或功能的其他技术描述。此部分讨论可替代性、生命周期、状态和抽象,以对粗粒度组件和细粒度组件进行大体区分。 语言中立性:能够被其他组件(可能采用其他语言)替换,而不会影响其使用者,或封装业务流程表现出高度的独立性的组件实现属于粗粒度组件。组件实现也可以替换为中介或网关模式,以便能够通过向交互中插入额外的值然后委托给实际服务实现来实现独立性。实现的灵活性是粗粒度组件的明显特性。在组件实现的上下文中,还有一些方面应该予以考虑。 容器独立性:在托管组件环境中,组件承载在容器中。有些容器以框架方法的形式强制将自身加入到组件的业务逻辑中。将框架与业务逻辑混合会限制用于承载组件的容器的设置。Enterprise JavaBeans (EJB) 2.0 或更高版本就属于此类组件模型。代码注释的引入在一定程度上减轻了此依赖性,但并未完全消除。注释的使用带来相同的容器依赖性问题,但不同的容器可能支持一组公用注释,如果业务逻辑对自身进行约束,仅仅使用公用注释,就可以与容器实现松散耦合。当框架混合到业务逻辑中时,这就不可能实现。细粒度组件通常不能在不同的容器间移植,因为可能会大量使用容器框架,而粗粒度组件通常移植性更好一些。 运行时生命周期:使用者知道(因而依赖于)运行时状态或服务提供者的准备情况,这就表明存在紧密耦合行为。交互模式 说明(上面)描述交互模式如何影响组件粒度。充当服务提供者的组件实现可以通过要求其使用者遵循提供者的运行时生命周期而影响组件间的耦合。这经常是通过结合通信量较多的接口甚至对话接口设计来完成的。向其使用者公开生命周期状态的组件实现通常被视为细粒度组件。 抽象:粗粒度组件应该直接映射到业务模型可识别且业务人员能理解的抽象。粗粒度组件通常通过组合细粒度组件来获得抽象的方式进行实现。IT 系统的用户(如银行出纳)经常知道业务模型中的粗粒度服务。粗粒度服务与 SOA 服务概念的对应关系更为紧密。细粒度组件数量更多,用于组织系统中逻辑的实现,通常实现详细的技术算法。 持久性:业务数据采用很多不同的方式进行持久存储。业务组件需要读取和操作这些数据,以执行其业务功能。细粒度软件组件经常使用针对特定持久性机制的 API,例如 Java™ 组件实现提供的用于访问关系数据的 JDBC。粗粒度组件趋向于使用其他组件服务(如数据服务)来获得业务数据。数据服务封装了持久性机制,从而允许使用服务的组件不受数据服务所使用的持久性机制的影响。尽管存在此处所指出的差别,但持久性机制的使用并不会对软件组件的粒度有很大的影响。持久性只是语言独立性甚至容器独立性的一个例子而已。 为了进一步说明前面所讨论的概念,让我们看一个 SCA 示例。以下示例说明了 BigBank 示例的 SCA 组装。在 SCA V1.0 规范中经常使用此示例进行相关说明。组装关系图添加了注释,以说明粒度的主要决定因素。 图 2. BigBank 示例 附录中提供了为 bigbank_account 组合使用 SCA 组合的组装和备选项的服务组件定义语言(Service Component Definition Language,SCDL)。每个备选项都在 粗粒度 SCA 组件模型具有以下粒度属性:
组件的非功能方面比功能方面更为抽象。单独而言,下面列出的非功能方面对粒度的影响小于前面讨论的功能方面的影响。非功能特征在用于定义粒度时更为主观,因此不能作为进行粒度量化的唯一标准。因此,以下列出的组件的非功能方面应该与其他非功能方面以及前面所述的功能方面结合进行评估。 组件的非功能方面包括(但并不一定仅仅有这些)组件在量方面的测定。下面的列表讨论了服务的延迟、可分布性、可伸缩性、占用空间和质量的内容,以便使用这些属性来区分粒度。与以下任意非功能特征相关都可能会因为硬件或软件的技术优势而更改甚至失效。 延迟:组件交互的预期响应时间非常重要,因为这可能会限制组件粒度的选择。通常,粗粒度组件的响应时间比细粒度组件长得多。这是由于与接口耦合相关的功能特征和其他非功能特征造成的。 位置:能够以物理方式分离各个组件而不会影响 IT 系统的行为,这种能力是松散耦合的粗粒度组件的关键属性。细粒度组件通常需要进行搭配,才能正确地操作,或者更常见的,来提供实现性能目标所必需的低延迟。经过搭配的细粒度组件通常表现为(或至少设计为)采用通信量较多 的交互模式。 可伸缩性:能够水平和垂直伸缩,这是不断发展的 IT 系统的一个非常重要的特征。通常,无状态交互模式的可伸缩性比其它模式更好一些。不过,由于硬件和软件的技术优势(特别是在缓存和状态复制方面)的原因,经常可以使用任何交互模式和使用任意粒度的组件(紧密耦合或松散耦合)构建具有高可伸缩性的系统。可伸缩性似乎与组件粒度没有什么关系。 占用空间:执行组件所需的虚拟内存的量可以决定其粒度。因为粗粒度组件通常都是由很多细粒度组件组成的,因此粗粒度组件执行消耗的内存与其所有组成组件消耗的总和相等。注意:因为粗粒度组件模型是独立于语言的,而且具有所讨论的一些其他特征,因此其内存占用系数可能比细粒度模型更大。由于粗粒度模型支持语言独立性和本地-远程透明性,因此其使用的内存中模型可能比支持特定编程语言并以其为中心的细粒度模型更为丰富。 QoS:QoS 的采用(安全性、可靠性、事务、服务水平协议等)是另一个重要元素。通常,粗粒度组件实现从 QoS 相关的 API 分离出来,依赖于其容器处理元数据或部署描述符,以提供所需的服务质量。这就提高了粗粒度组件在 QoS 上下文中的可配置能力。细粒度组件趋向于绑定到特定的 Qos API,而这会对实现可替代性和传输独立性的功能造成限制。很多组件模型都支持第三方拦截的概念,此概念支持引入原始运行时供应商未提供的扩展服务质量。通常,细粒度组件之间的这种类型的拦截可能会导致性能问题,从而通常在粗粒度边界上进行。不过,利用这些扩展 QoS 功能会产生与组件实现使用内置 QoS API 相同的紧密耦合效果。
SCA 编程模型的主要用例涉及到将业务功能抽象为松散耦合的服务的能力,这些服务由粗粒度组件、细粒度组件的组合以及粗粒度组件的组合实现。SCA 通过两种方式提供组合:通过引用组合和通过实现组合。SCA 组件可以通过独立于服务的部署位置和方式的单个调用编程模型编排这些服务的调用模式来组成服务。SCA 还可以通过使用细粒度组件的组装作为粗粒度组件的实现来提供通过实现组合 (composition by implementation)。细粒度组件位于通过实现组合的“叶子”的位置,而粗粒度组件位于其“根”部。SCA 示例部分中的示例应用程序演示了这两种组合形式。 表 1 将 SCA 传统 Java 对象(plain old Java object,POJO)编程模型的主要特征划分为了粗粒度和细粒度两个方面。要满足粗粒度组件模型的需求,细粒度列中的所有项目都必需视为 SCA 组件模型的一部分。此表基于 Open Service Oriented Architecture (OSOA) Collaboration 所制定的 V1.0 规范。 表 1. SCA 编程模型粒度
其他组件模型中有很多 SCA V1.0 中所没有的特性和功能(例如,持久性机制、线程语义等等)。之所以会出现这种情况,是因为 SCA V1.0 主要关注的是粗粒度组装的缘故。
表 2 根据其用于细粒度组件模型还是粗粒度组件模型对各个组件技术进行了大体归类。此表的重点在对粒度有最大影响的方面,即抽象、数据格式、传输独立性、交互模式和中介。 表 2. 组件模型比较
组件模型可在构建软件系统时提供价值。这个价值以抽象和支持组合的形式表现。为了提供良好的抽象,组件模型必须封装组件可能需要的各个实现方面的细节。 要对组件粒度进行分类并了解如何利用组件模型,这并不困难。本文提供了一些关于组件及其所属的组件模型的各个方面和属性的背景信息,特别对粒度进行了讨论。 细粒度组件具有一组特定特征,允许在系统的整个逻辑实现中将其作为组织模式使用。如果您的应用程序组件需求包括毫秒级的响应时间、低延迟、处理按引用传递语义的功能、假定同步处理且紧密耦合到调用方的执行上下文(语言、QoS、位置),则您的组件是细粒度组件,应该使用细粒度组件模型进行开发。 粗粒度组件在业务系统中只有较少量的抽象。粗粒度组件与 SOA 中的服务具有更为直接的对应关系,与业务模型中可识别的内容的对应性更好。如果您的应用程序组件需求包括要独立于调用方的执行上下文(语言中立性、有条件的独立 QoS 和位置)、可在各种上下文中重用、支持异步处理或不允许按引用传递语义,则您的组件是粗粒度组件。需要采用粗粒度原则对其进行设计,包括允许毫秒级(或更长时间)的延迟、交换大型的面向文档的数据集、支持通过较少的交互进行较多的工作以及消除对通信技术的任何依赖性。 SCA 是最适合粗粒度组件的模型。SCA 支持各种组件实现技术,能够在这些异类技术之间进行连接。支持异步和同步交互样式,并支持各种通信技术。基础设施功能通过策略应用到组件及其间的连接,这也是一个优势,可支持在各种上下文中使用组件。通信中介也建模在 SCA 中。 SCA 的优势之一就是能够与用于实现粗粒度服务组件的各种细粒度组件模型结合使用。从建模整体 解决方案的结构的角度而言,SCA 会给各种形式的组件带来好处,提供所需的敏捷性和灵活性,并消除在实现代码中定义复杂配置细节的需求。另外,它还能够对用于总体解决方案的不同部分的不同组件模型进行连接。
清单 1 是 bigbank 组合的 SCA 组合。请注意, 清单 1. BigBank 组合
清单 2 是 bigbank_account 组合的 SCA 组合。 清单 2. Account 组合
清单 3 是 清单 3. POJO Account 组件
清单 4 是 bigbank_account 组合的 Spring Bean 组装。此代码的目的是为了说明可以将 Spring 组装作为粗粒度组件实现使用。 清单 4. Spring Bean 组合
学习
获得产品和技术
讨论
|