使用服务组件架构(SCA)从不同技术调用组件
 

2009-12-15 作者:Dan Becker 来源:IBM

 
本文内容包括:
服务组件架构(SCA)是一项可以抽象底层计算机服务的新技术,这样,系统就可以更加灵活地合并由不同技术实现的子系统。SCA 将业务逻辑中有关访问技术、实现和协议的大量细节移动到中间件层中。这种抽象对于某些开发人员来说是有代价的。因为业务应用程序将变得难于理解和调试。通过本文了解如何针对不同的协议绑定和实现类型执行组件调用。本文的示例使用了开源的 Apache Tuscany SCA 运行时来解释底层复杂性。

简介

在商业世界中有两个重要的驱动因素:变化和异构性(heterogeneity)。不论您当前使用何种业务解决方案,都必须改变它,添加新特性,进行更新以获得更好的服务,或者进行修改以满足供应商的要求。这些驱动因素对加快业务解决方案的面市产生了巨大的压力。企业从头到尾设计大型项目并控制所有子系统的时代已经一去不返了。

SCA 是应对变化性和异构性的一项新技术。通过大量抽象底层实现技术,SCA 使系统在合并使用不同技术实现的子系统时可以更加灵活并迅速适应变化。SCA 将业务逻辑中有关访问技术、实现和协议的大量细节移动到中间件层中。

一些技术架构师和开发人员在实现样例 SCA 或更新现有服务方面迈出了第一步。随着技术消费者的日渐成熟,仍然存留了大量问题,比如:

  • SCA 组件之间究竟是如何通信的?
  • 当一个客户机程序与我的 SCA 应用程序通信时,SCA 运行时如何对我实现调用方法?
  • 当我的 SCA 应用程序遇到问题时,如何解释运行时调用堆栈上出现的各种类?

在本文中,将解答以上这些问题。

SCA 运行时

本文使用 Apache Tuscany SCA 运行时环境作为示例,原因如下:

  • Apache Tuscany 是 SCA 的一个开源实现,并且可以免费下载。您可以安装 Tuscany 和 Eclipse 开发平台 来实践本文的示例。
  • Tuscany 是独立于供应商和平台的。它可以独立运行,也可以与来自不同供应商的服务器(Tomcat、JBoss、Geronimo、IBM® WebSphere®、WebLogic)配合使用,并且可以在多个平台上使用(Microsoft® Windows®、Linux® 和 Mac OS)。
  • Tuscany 使用了本文讨论的诸多协议绑定和实现。Tuscany 是适合解释如何对 SCA 开发人员隐藏底层实现复杂性的优秀平台,使您能够将精力放在修改并合并各种异构子系统上。

基本 SCA 概念

如果您对基本的 SCA 术语不了解,那么本节将简要介绍许多在 SCA 实现中共享并且在最新 Open SOA 规范 中标注的术语。

SCA 应用程序由集合(assembly)执行建模。一个 SCA 集合 包含一组工件以及将这些工件链接在一起的连接。集合存储在一个复合文件(composite file)中。复合文件 常被描述为一个 XML 文件,其中包含各种 SCA 片段及它们之间的连接。复合文件被载入并准备好在 SCA 运行时(例如 Tuscany)中运行。图 1 展示了一个示例集合。

图 1. 典型 SCA 集合
典型 SCA 集合

组件 是指一个基本的 SCA 工件,是业务应用程序的构建块。它提供基本的业务功能,比如股票报价服务或计算器服务。组件可以具有一些属性来控制业务服务的行为,并且提供组件的配置。一个 SCA 组件可以通过多种技术实现。SCA 规范定义了一组基本的实现类型(Java™ 技术、BPEL、J2EE 和 Spring),并且允许其他 SCA 组件进行递归组合。Tuscany 提供了额外的类型,包括脚本语言(JScript、Groovy、PGP、Python、Ruby)和 OSGi。实现类型用于合并异构子系统和重用现有代码。

SCA 组件的功能被作为服务提供给其他组件;一个 SCA 组件可以通过一个引用(reference)来利用服务。服务和引用被关联在一起并显示出来,并通过推广发布到外部世界中。每个服务或引用都有一个接口,将业务接口描述为一组方法。每个方法有一个名字、若干参数和一个返回值。SCA 使用一个 Java 语言接口或通过一个 Web Services Description Language (WSDL) 文件定义这些接口。每个服务和引用通过一个访问绑定 来访问。绑定定义了用来访问服务或引用的协议。目前,SCA 规范包含了一组基础的流行绑定技术,比如 HTTP、JMS、Web 服务和 SCA(通常在两个组件运行在相同 Java 虚拟机上并且没有特别指定绑定时使用)。

Tuscany 提供了额外的一组有用绑定,帮助支持 Web 2.0 风格的应用程序、Atom、RSS、JSON/RPC 等等。如图 2 所示,Tuscany 负责从各种技术中获取特定于协议的消息,并将之转换为业务组件可以使用的形式。业务组件实现对这些数据进行处理并传递回 Tuscany SCA 运行时。再强调一次,我们的目标是合并并重用现有的访问技术,同时让业务组件开发人员能够将侧重点放在业务功能方面。

图 2. SCA 运行时堆栈
SCA 运行时堆栈

最后,在调用业务接口中的每个方法时,作为参数提供给方法并从调用返回的数据将通过数据绑定提取。常见数据类型包括 Java 对象、XML 文档和服务数据对象(Service Data Objects,SDO)。Tuscany 可以在不同数据类型之间自动转换,为提供者和客户机之间的数据流动提供便利。但是本文的重点并非数据绑定,而是这一术语的各种应用。

本节重点介绍 SCA 为业务应用程序开发人员提供的各种抽象。接口当前使用 Java 语言或 WSDL 指定。访问绑定使用多种协议(HTTP、JMS、Web 服务、Atom、JSON/RPC)实现。组件实现可以通过多种技术(Java 技术、BPEL、SCA、Spring、J2EE、Scripting 语言、OSGi)获得。本文余下的部分将解释这些技术如何交互,从而使您理解从服务调用到实现中的业务方法调用这一底层路径。

场景

本文示例展示了一个简单业务应用程序的演变过程。您可以 下载 场景示例的源代码(应用程序很简短,并且针对示例的目的进行了简化)。

对这个业务服务的惟一要求就是将两个数字相加。发布计算器服务供客户机调用。此计算机被连接到只有一个功能的 add 服务:将两数相加并返回总和(例如
double add(double o1, double o2) )。假设接口使用 Java 语言指定,并且服务也通过 Java 语言实现。图 3 展示了这个 SCA 集合。

图 3. 计算加法的 SCA 计算器组件
计算加法的 SCA 计算器组件

指定了计算器组件和它的实现后,这个组件将引用 add 服务组件及其实现。清单 1 展示了此业务应用程序的 SCA 复合文件。可 下载 该组件及其他组件的源代码。

清单 1. 计算器复合文件
 
				
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" 
   xmlns:sample="http://sample" 
   name="Calculator">

   <component name="CalculatorServiceComponent">
      <implementation.java class="calculator.CalculatorServiceImpl"/>     
      <reference name="addService" target="AddServiceComponent"/>
   </component>
  
   <component name="AddServiceComponent">
      <implementation.java class="calculator.AddServiceImpl"/>
   </component>
</composite>

假设客户需求增长,并且需要一项可以计算减法的新服务。然而,有一种 Web 服务可以将接口指定为 WSDL 文档并且被实现为一个 Java 程序。此 Web 服务可以通过 Web 服务访问绑定进行访问。因此,通过添加一个引用和访问减法服务的组件,就可以轻松地向 SCA 复合文件添加新的需求。

清单 2. 包含新服务的计算器复合文件
 
				
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" 
   xmlns:sample="http://sample" 
   name="Calculator">

   <component name="CalculatorServiceComponent">
      <implementation.java class="calculator.CalculatorServiceImpl"/>     
      <reference name="addService" target="AddServiceComponent"/>
      <reference name="subtractService" target="SubtractServiceComponent"/>
   </component>
  
   <component name="AddServiceComponent">
      <implementation.java class="calculator.AddServiceImpl"/>
   </component>
   
   <component name="SubtractServiceComponent">
      <implementation.java class="calculator.SubtractServiceImpl"/>
      <service name="SubtractService">
        <interface.wsdl interface="http://calculator#wsdl.interface(SubtractService)" />
        <binding.ws uri="http://localhost:8080/SubtractServiceComponent"/>
      </service>
   </component>

</composite>

要实现业务示例的演变,我们需要添加对乘法和除法服务的需求。这些服务可以以 JScript 和 Groovy 实现的形式获得。Tuscany 允许您轻松地将这些服务添加到复合文件中。业?应用程序表示现在应当如图 4 所示。

图 4. 包含 4 个服务的 SCA 计算器
包含 4 个服务的 SCA 计算器

在应用程序的最后一个 SCA 复合文件中,注意添加的 JScript 和 Groovy 组件是对 SCA 规范的特定于 Tuscany 的扩展。因此,一个新的名称空间属性为这些组件引入了特定于 Tuscany 的语法。

清单 3. 包含 4 个服务的计算器复合文件
 
				
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" 
   xmlns:sample="http://sample" 
   xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0" 
   name="Calculator">

  <component name="CalculatorServiceComponent">
     <implementation.java class="calculator.CalculatorServiceImpl"/>
     
     <reference name="addService" target="AddServiceComponent"/>
     <reference name="subtractService" target="SubtractServiceComponent"/>
     <reference name="multiplyService" target="MultiplyServiceComponent" />
     <reference name="divideService" target="DivideServiceComponent" />
  </component>
  
  <component name="AddServiceComponent">
    <implementation.java class="calculator.AddServiceImpl"/>
  </component>
  
  <component name="SubtractServiceComponent">
     <implementation.java class="calculator.SubtractServiceImpl"/>
     <service name="SubtractService">
        <interface.wsdl interface="http://calculator#wsdl.interface(SubtractService)"/>
        <binding.ws uri="http://localhost:8080/SubtractServiceComponent"/>        
     </service>
  </component>

  <component name="MultiplyServiceComponent">
     <tuscany:implementation.script script="calculator/MultiplyServiceImpl.js"/>
  </component>

  <component name="DivideServiceComponent">
     <tuscany:implementation.script script="calculator/DivideServiceImpl.groovy"/>
  </component>
</composite>

该场景可以让您做出两项快速修改,并向初始业务应用程序添加三个组件。新的组件将在初始组件的基础上使用不同技术实现和访问。SCA 可以对变化的和不同种类的业务需求做出响应。

Tuscany 运行时简化了对这些技术的访问,并且 SCA 复合文件相对来说较少包含特定于技术的内容。然而,我们并不了解这些技术在底层是如何调用的。现在让我们深入探究一下 SCA 的内部。

Tuscany 调用架构概述

SCA 包含大量有关访问技术、实现和协议的细节,并将它们从业务逻辑转移到中间件层。这对于业务应用程序开发人员来说是件好事,但是对于软件开发人员有什么影响呢?本节将探讨 Tuscany 框架的内部,展示业务组件实现中的典型调用路径。

要运行 SCA 业务应用程序,SCA 运行时(比如 Tuscany)首先需要加载并配置 SCA 复合文件。对各个复合文件工件进行检查,然后使用工厂方法在内存中实例化不同对象。第二步是实例化用来连接组件的运行时连接(runtime wire)。在这个步骤中,将通过复合文件中提到的绑定针对组件引用和组件服务创建运行时连接。

运行时连接 是一个调用链集合;业务接口中的每个方法都有一个调用链。将创建一个处理程序,它充当一个消息交换台操作程序(switchboard operator)并将每个方法调用传递到相应的调用链。每个调用链由一组调用器(invoker)和拦截器(interceptor)组成。调用器 为绑定协议和实现技术提供调用逻辑。拦截器 是一种特殊的调用器,它提供其他的功能,比如数据转换、安全性和事务控制。对于组件引用,将创建一个运行时连接来通过所选的绑定表示出站(outbound)调用。对于组件服务,将创建一个运行时连接来表示对实现类型的入站(inbound)调用。回调链也被添加到运行时连接中以提供对组件的反向回调。

当消息从计算器组件传递到 add 组件时,遵循计算器应用程序中的调用路径。

  1. 一个调用被分配到 InvocationHandler。在本例中,这是一个基于 Java 技术的处理程序。
  2. InvocationHandler 查找正确的 InvocationChain。本例查找 add 方法链。
  3. 各种调用器和拦截器创建一条消息,设置有效负荷,设置 TargetInvoker,并将消息沿着调用链传输。
  4. TargetInvoker 通过底层协议发布消息。一个 SCA 组件通过参照另一个 SCA 组件传递一些 double 值。因此没有出现数据转换或协议转换。
  5. 在接收端,一个侦听程序将从底层协议获取消息并将其传递给其中一个接受组件的调用链。再次强调,SCA 组件是使用 Java 技术实现的。将创建与 add 方法对应的链。
  6. 调用链找到 add 服务的 add 方法并调用此方法。组件实现将数字相加并返回总和。
  7. 反向路径被送回到调用组件。

此路径如下所示。

图 5. 从源到目标的 SCA 调用
从源到目标的 SCA 调用

Tuscany Java 到 Java 的调用

当使用调试器调试示例 SCA 应用程序并在 AddService add 方法上设置断点时,将看到如清单 4 所示的调用堆栈跟踪。计算器服务实现调用方位于顶部。add 方法调用服务的 Java 代理表示。JDKInvocationHandler 将消息传递给合适的调用链。特殊的 PassByValueIntercepter 查找 SCABindingInvoker 并通过它传递消息。SCABindingInvoker 在服务端查找镜像 PassByValueInterceptor。消息被转换为 Java 调用,后者将调用 add 服务组件实现。

清单 4. Java 调用堆栈
 
				
CalculatorServiceImpl.add(double, double) line: 54

$Proxy7.add(double, double) line: not available	
JDKInvocationHandler.invoke(Object, Method, Object[]) line: 155	
JDKInvocationHandler.invoke(InvocationChain, Object[], RuntimeWire, EndpointReference) 
   line: 287	
PassByValueInterceptor.invoke(Message) line: 112	
SCABindingInvoker.invoke(Message) line: 61	
PassByValueInterceptor.invoke(Message) line: 112	
JavaImplementationInvoker.invoke(Message) line: 132	
Method.invoke(Object, Object...) line: not available	
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available	
NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available	
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available

AddServiceImpl.add(double, double) line: 30	

从计算器服务向 add 服务发出一个简单的 add 调用似乎使用了一个很长的调用链!确实很长,但是这种从业务领域到底层技术领域的抽象在每个业务应用程序中都会出现。如果您要自己实现这类特性,那么必须肩负起实现和维护这一层的重担。

Apache Tuscany 的 SCA 运行时提供了这种实现并为您完成了这些工作。它具有如上所示的简单的 Java 到 Java 的调用以及大量服务和引用绑定,具有许多组件实现类型。技术实现被从业务逻辑中分离出来,因此大大简化了应用程序。

Tuscany Java 到 WSDL 的调用

这个来自 SCA 应用程序的示例具有更加复杂的调用,包括计算器服务和减法服务方法等等。回想一下,两种服务都使用 Java 技术实现,但是在这里,减法服务使用 WSDL 接口描述,而调用通过一个 Web 服务访问绑定运行。SCA 使您可以引用并访问各种不同的技术。

在本例中,应用了相同的一般调用路径,但是路径中出现了更多的拦截器。Tuscany 运行时现在必须将基于 Java 技术的调用转换为底层协议的 Web 服务消息。例如,将调用 DataTransformationInterceptor 将 double 值的 Java 表示转换为原生的 Web 服务 XML 表示。图 6 展示了对目标组件的源组件调用的多个阶段,并且图 5 中不同颜色显示的每个阶段表示其在调用中的角色。

图 6. 含有中间类的 SCA 路径
含有中间类的 SCA 路径

再重申一次,如果将这个计算器 SCA 应用程序和 Tuscany 运行时放到 Java 调试器中,并且对服务的 subtract 方法设置了一个断点,那么将看到类似清单 5 的输出(这个推展仅展示了调用的接收端)。Web 服务通常位于 HTTP 协议层之上,而 HTTP 层以请求的形式接收到一个消息调用并将此请求传递给底层 Web 服务层。

在堆栈的最上端,底层技术 Web 服务引擎接收调用。由于 Apache Tuscany 使用 Axis2 Web 服务引擎,可以看到在堆栈中有几个 Axis2 类。Tuscany RuntimeWire 调用器随后接收本地消息并找到合适的调用链来执行调用。此调用首先调用 DataTransformationInterceptor,这将把 Web 服务表示转换为一个 Java double。JavaImplementationInvoker 查找并对 subtract 服务调用 Java subtract 方法。

清单 5. Web 服务调用堆栈
 
				
AxisEngine.receive(MessageContext) line: 176	
Axis2ServiceInOutSyncMessageReceiver(AbstractMessageReceiver).receive(MessageContext) 
   line: 100	
Axis2ServiceInOutSyncMessageReceiver(AbstractInOutSyncMessageReceiver).
   invokeBusinessLogic(MessageContext) line: 42	
Axis2ServiceInOutSyncMessageReceiver.invokeBusinessLogic(MessageContext, MessageContext) 
   line: 70	
Axis2ServiceProvider.invokeTarget(Operation, Object[], MessageContext) line: 736	

RuntimeWireImpl.invoke(Operation, Message) line: 158	
RuntimeWireInvoker.invoke(Operation, Message) line: 98	
RuntimeWireInvoker.invoke(RuntimeWire, Operation, Message) line: 104	
RuntimeWireInvoker.invoke(InvocationChain, Message, RuntimeWire) line: 129	
DataTransformationInterceptor.invoke(Message) line: 78	
JavaImplementationInvoker.invoke(Message) line: 132	
Method.invoke(Object, Object...) line: not available	
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available	
NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available	
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available

SubtractServiceImpl.subtract(double, double) line: 27	     

结束语

尽管示例 SCA 应用程序在概念上非常简单,但是其实现却相当复杂。计算器应用程序涉及 4 个不同的服务。每个服务通过不同的技术(Java 技术、Web 服务、HTTP、JScript 和 Groovy)实现或访问。对于某些架构来说,可能不允许将这些完全不同的技术集成在一起。技术层和业务层可能一直都是混合在一起的。一些开发人员将花费大量时间来从某个技术领域转移到另一个他们认为是最好的技术领域。

SCA 与上述架构完全不同。它允许您利用现有的技术并将它们合并到同一个应用程序中。这些技术按原样导入,几乎很少进行转换和重新编码。并且技术实现与业务逻辑被分离开来。任何一个 SCA 组件都可以换入或换出,或者托管在新的绑定或实现中,而且对整个应用程序的影响非常小。

付出很小的代价就能获得 SCA 的这些优势。幸运的是,Tuscany 等的 SCA 运行时可以为您完成消息传递、理解各种接口、与访问和数据绑定交互,以及调用各种实现类型等全部工作。实际上,应用程序中的许多技术问题被转移到了 Apache Tuscany 中间层中。有时,当在 SCA 应用程序中查找问题时,生成的调用路径看起来有些复杂。本文通过一个 SCA 示例实现帮助您理解这些复杂性。

下载

描述 名字 大小 下载方法
展示各种调用的日历计算器
os-apache-tuscany-sca-sample-calculator.zip
13KB

参考资料

学习
  • 了解 Apache Tuscany,这是面向 SOA 部署和管理的综合基础设施,它基于 Service Component Architecture (SCA) 标准。
  • 开放 SOA 服务组件架构规范 包含了最终版规范,也包含了代表最新版本的规范草案。
  • 浏览 技术书店,阅读有关这些主题和其他技术主题的图书。
  • 要收听针对软件开发人员的有趣访谈和讨论,请访问 developerWorks podcasts
  • 随时关注 developerWorks 技术活动网络广播
  • developerWorks on Twitter 获取知识。
  • 查阅最近将在全球举办的面向 IBM 开放源码开发人员的研讨会、交易展览、网络广播和其他 活动
  • 访问 developerWorks 开源专区,获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。
  • 查看免费的 developerWorks 演示中心,观看并了解 IBM 及开源技术和产品功能。
获得产品和技术 讨论

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织