内容


SOA 探索,第 2 部分

使用活动对象模型解决复杂业务事务的挑战

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: SOA 探索,第 2 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:SOA 探索,第 2 部分

敬请期待该系列的后续内容。

引言

虽然面向服务的体系结构(Service-Oriented Architecture,SOA)——特别是 Web 服务——通常旨在支持广泛的消息交换模式,但是当今的现实是大多数生产实现都基于请求-响应和单向模式。诸如“信用卡授权”、“税款计算”、“获取项目”、“查找图书”、“检查股票”等简单的短时间请求-响应服务占据支配地位。

出现这种情况的原因有几个。当然,其中之一是 Web 服务通常是使用不可靠的无状态传输协议(明确地说是 HTTP)来实现的。但更重要的是,一些希望通过 Web 来执行复杂业务事务的公司对此问题采取了一种太“技术相关”的观点,并预期 Web 服务技术和相关供应商产品完全支持这样的工作。

诸如 OASIS 等标准机构和诸如 IBM®、Microsoft®、Oracle、BEA 和等领先的 SOA 供应商认识到了处理所谓的“对话”消息交换模式的需要,该模式与 Web 服务实现中的实际业务事务(或操作系列,它们定义诸如按次序处理或保险报价处理等内聚的多步骤交互)等效。后来,出现了多个 Web 服务规范,例如 WS-Addressing、WS-Resources 和 WS-Coordination 以及 WS-Transaction。而且最重要的是,业务流程执行语言(Business Process Execution Language,BPEL)1.0 规范已经整合了新的“WS-”标准建议的许多用于处理复杂业务事务的功能。然而,和许多与安全相关的 Web 服务标准不同,处理 Web 服务对话的新标准和相关供应商技术还没有得到广泛使用。此外,在当前版本中,BPEL 本身在这个特定方面具有显著的缺点。

同时,Web 服务设计人员正在面临挑战,他们需要在更多类型的传输协议(例如,HTTP、SNMP 等等)、更多类型的客户端设备上满足更多用户对 Web 上所有类型的实时交互的需要——例如,获取有关最合适产品的建议,而不只是在线购买商品。要应对此类挑战,将需要对服务(甚至是对那些服务的基础——它们的组件和对象模型)的设计进行日常维护。

支持复杂业务事务的最有效方法之一是使用所谓的以活动为中心的服务——这些服务是明确地为处理定义良好的任务或流程而设计的,并通过维护特定活动使用的所有应用程序的计算状态来完成其工作。

引入以活动为中心的服务和活动对象

概括地说,以活动为中心的服务旨在执行单个活动,此活动涉及到许多不同的应用程序(或系统)和每个应用程序中的大量特定功能和数据表示形式。以活动为中心的服务的主要特征之一在于这些服务提供了执行保证,这不仅限于传统的 ACID(原子性、一致性、隔离性和持久性)属性,并且可以包括时间上的约束、状态访问灵活性和多重交互的事务执行。这些保证不仅可能在应用程序之间有所不同,而且可能在一个应用程序中的不同事务之间也有所不同,具体取决于事务任务的属性、用户首选项和可用的系统资源。

图 1 中的示例场景(搜索大量航班以查找最低票价的旅行预定服务)说明了这一点。在此示例中,客户端提供多组起飞和目的地信息,该服务搜索相关的航线以了解可用航班和票价。如果没有可用航班,则客户端与服务之间的交互必须停止。否则,该服务将向客户端返回航班和票价列表。然后,如果客户端从列表中选择某个特定航班,则该服务将要求客户端提供所需的私人信息以完成交易,例如姓名、地址和信用卡信息。此时,将执行若干任务。首先,将根据安全和信用卡授权规则验证所提供的信息,并在所提供的信息存在问题时通知客户端。如果所提供的私人信息可接受,则该服务将尝试预订航班。如果预订成功,则创建客户旅程记录,并向客户端返回该记录的标识符以便旅行者将来使用。

图 1. 示例场景——查找最低票价
图 1
图 1

如果从计算的角度查看这里引用的示例,该服务实际上是在处理多个不同的应用程序:查询航线可用性、验证客户信息、预约航班、创建旅程等等。因此,您至少可以确定两个级别的服务抽象,即高级活动和由活动所操作的低级应用程序服务。我将这两个服务抽象级别分别表示为以活动为中心(业务)的级别和应用程序(离散)级别。从上面的示例中可以看到,以活动为中心的服务引入了多个关键技术要求,特别是:

  • 服务上调用的进程密集型方法不应该阻塞整个进程。
  • 对共享资源的同步访问应该允许透明的序列化和调度。
  • 服务应该设计为利用并行性(例如,在该示例中,可能存在数千个需要快速搜索的航班地点)。

首先,“以活动为中心的服务”的概念是在活动服务框架 (Activity Service Framework) 规范中定义的,OMG 为响应自己对用于对象事务服务(Object Transaction Service,OTS)的专门结构化机制的号召而提交了该规范。最近,这个概念正在重新引入网格计算领域。这里,规定了附加的技术要求,包括

  • 以活动为中心的服务应该能够响应异步事件(例如,动态地向事务添加新的参与方的能力;例如,可能需要自动监视客户在基于 Web 的店面上的会话,并通过允许客户代表加入交互过程来主动进行干预)。
  • 在组合以活动为中心的服务时,组合引擎必须能够接收异步事件,并确定如何和是否在服务组合中影响参与者(此功能是 BPEL 引擎的最重要功能之一)。
  • 以活动为中心的服务的基础结构必须支持服务之间跨越时间和空间的通信和协作。

因此,以活动为中心的服务的通用对象模型已通过以下代码行浮现出来:

清单 1. 以活动为中心的服务对象模型
public interface Activity {
    public ActivityId execute(ActivitySpecification actSpec,
                        ClientSpecification clientSpec,
                        RemoteEventListener callback)
        throws RemoteException, ActivityException;
    // other methods ...

人们建议了许多对象模型来实现以活动为中心的服务,其中最有趣的是活动对象模型。遗憾的是,该模型的几个问题阻止了它在商业环境中的广泛采用。首先,有些人对活动对象究竟是什么感到混淆;基本上,四处流传着活动对象的太多不同定义。其次,有些所谓的“SOA 专家”正在鼓吹“用于分布式对象的活动对象模式超出了 Web 服务的功能”。实际上,此类观点言过其实。诚然,实现活动对象需要费一番工夫,但是这样的对象支持构建并发应用程序,此类应用程序将在不断发展的 SOA 运动中高度适用。业界已从紧密耦合的 RPC 风格的同步服务转向松散耦合的异步服务。

Grady Booch 于 1990 年在其有关面向对象的应用程序设计的书中最先引入了活动对象。他将活动对象定义为具有自己的控制线程(或自己为自己提供支持)的对象。从那以后,活动对象在 Java™ 移动版本和实时嵌入开发中赢得了大量的立足之地。然而,使用它们来实现大规模电子商务应用程序中的对话 Web 服务同样可以成功。对于这些用途,首先应该将活动对象明确视为能够并发运行的对象(相对于作为服务的一部分来运行的其他对象而言),其次(并且最重要的是),活动对象能够在与某个客户端相关的整个生命周期过程中维护服务的内部状态。此外,活动对象能够通过组合关系来聚合被动对象。

就实现而言,活动对象符合以下形式:

清单 2. 以活动为中心的服务对象类
class ActiveObject implements Runnable { private Thread activity;
public ActiveObject() {
activity = new Thread(?);
activity.start();
}
public void run() { ... }
}

虽然从结构上讲,可以存在多种活动对象表示形式,但是在本文的上下文中,最适当的是包括以下七个组件的模型(如图 2 所示):

  • Requester 或 Proxy——提供用于调用活动对象上的方法的接口。
  • Servant——实现与请求者的接口和请求者创建的方法请求相对应的活动对象。
  • Method Request——包含诸如参数等上下文信息,并表示对活动对象的方法调用。
  • Activation Queue——维护等待执行的方法请求。
  • Future——存储活动对象上的方法调用的结果。
  • Scheduler——将方法请求插入激活队列。
  • State Machine——描述活动对象的行为。
图 2. 活动对象模型——类关系图
图 2
图 2

本文已非常深入地讨论了这个话题,下面让我们重申一下到目前为止已看到的难点:

首先,当我们在 SOA 中处理活动对象的时候,体系结构范式是一项策略,其基于“面向服务的系统具有两个不同部分”的概念:面向对象和面向服务的模型。

面向对象的模型将系统定义为一组交互的主动和被动对象的集合。这些对象包含系统的功能和数据。然后,在面向对象的模型基础上建立了面向服务的模型,其中活动对象变为网络化的对象,它们扮演预定义的服务请求者或服务提供者角色。服务提供者是接受来自服务请求者的消息以执行某项工作(某个任务或活动)的对象。

其次,由于以下原因,活动对象从长期的角度来看对 SOA 特别有吸引力:

  • 它们解决了大规模分区和并发问题。
  • 它们根据需要进行通信以完成工作,按自己的计划封装了要做什么、如何做以及何时做,并带有清楚的“从运行到完成的工作方式”语义。
  • 它们高度模块化,非常易于调度。

用于实现活动对象的实用方法

存在多种使用活动对象来实现以活动为中心的服务的方法。其中,最简单和相当有效的方法如图 3 所示。

图 3. 实现示例——结构
图 3
图 3

图 3 引用的方法具有三个不同的部分:处理消息的服务访问层 (SAL)、提供服务分派和管理的服务管理层 (SML),以及业务对象层 (BOL)。SAL 和 SML 都实现了一个活动对象模型。实现为 EJB 的业务对象扮演被动对象的角色。将被动对象实现为 EJB 是首选的,因为它为服务提供好得多的可用性和可伸缩性特征。

此时,我将重点介绍用于实现活动对象模型的特定设计技术。遗憾的是,由于空间所限,我将仅介绍与本文主题相关的最重要元素。

您可以使用 Apache Axis 框架来构建 SAL。这里,我假设您对 Axis 体系结构和 Axis 的工作方式具备基本的了解(请参阅参考资料以获得指向 Axis 网站的链接)。

通过选择 Axis,处理消息和使用消息来互连活动对象就变得容易多了。Axis 提供了许多重要的功能,包括处理程序、链、序列化器和反序列化器。它允许创建适当的 MessageContext,并通过一组已配置的处理程序将 MessageContext 传入 invoke() 方法,其中每个处理程序可以对 MessageContext 执行必要的逻辑。

在实现活动对象模型时涉及到的最重要处理程序类型是提供程序处理程序。提供程序是处理传入请求的特殊 Axis 处理程序。Axis 提供了许多不同的提供程序。可以在部署新服务时指定应该对给定的 Web 服务使用哪一个提供程序。Axis 的提供程序模型是可扩展的,从而意味着您可以插入自己的自定义提供程序。清单 3 显示了用于将服务部署到 Axis 中的典型 WSDD 文件。它表明某个自定义提供程序将处理传入请求:

清单 3. Axis WSDD 部署示例
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
      xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="LowestFareService" provider="java:LowestFareProvider" />
</deployment>"

图 3 所示,可以构造提供程序来扮演实现 Runnable 接口的异步调用处理程序的角色。该接口使用 run() 方法在处理程序的控制线程中处理各个请求。此外,提供程序将所有必要的信息放入新的 EventMessage 中,后者被传递给 SML 的入口点 Manager。EventMessage 将有关调用处理程序上的特定服务调用的上下文信息(例如方法参数和代码)从处理程序传递到在单独线程中运行的 Manager。

事件类型的消息不应该包含任何特定于 SOAP 的数据。这样允许将活动对象的实际实现与 Axis 和 SOAP 协议分离。

图 3 所示,一旦接收到来自 Manager 的回调,提供程序将创建一个新的 SOAP 响应消息,此响应消息重新回到 Axis 响应流,并最终从那里被发送回调用服务的客户端。回调功能是提供者的最重要功能之一。它取得作为消息进行传递的 onFuture() 方法的结果(请参见图 4 的讨论),并将其转换为 SOAPMessage 实例。

图 4 是协作关系图,其中给出了 SML 中涉及到的最重要类的粗略图形表示形式:

  • Manager——扮演活动对象模型的代理角色;通过使用 EventMessage 的实例,Manager 确定放入激活队列中的服务调用请求。此队列跟踪要执行哪些服务请求。
  • Dispatcher——管理等待执行的服务请求的激活队列;它决定接下来哪个请求退出队列并在实现该请求的 Service Delegate 对象上执行。
  • Service Delegate——封装对 EJB 的访问,类似于 Sun® 的 J2EE Blueprints 所引入的 Business Delegate 模式;定义被建模为活动对象的行为和状态;Service Delegate 在 Dispatcher 执行对应的服务请求时被调用。
  • Future——允许 Manager 在 Service Delegate 完成执行后获得服务调用的结果;Future 为 EJB 调用的结果保留空间。
图 4. 实现示例——协作关系图
实现示例——协作关系图
实现示例——协作关系图

Activation Queue 是一个事件处理类型的队列,它可以收集来自多个提供者的事件。Dispatcher 按先进先出 (FIFO) 的顺序分派事件。可以使用向量来实现此队列,但是更高效的实现是使用链表或管道流。为了将到达的服务请求放入队列中,Manager 实现了 pushRequest() 方法,如下所示:

public synchronized void pushRequest(Object eventMessage);

pushRequest() 可以定义到达的事件是否要由某个回调方法进行处理。如果是,则调用 replyImmediate(),否则,将到达的事件压入队列。此外,Manager 调用 notify() 以唤醒服务委托的线程,以便能够处理到达的事件。然后,它必须等待使用关联的服务委托对象的结果来填充所有的 Future,但这是在后台进行的。此外,Manager 可以在内部实现某种类似如下的等待循环:

清单 4. 等待循环示例
public synchronized Object get() {
  while(futureResult==null) wait();
  return futureResult; 
}

结束语

您可能忍不住匆忙地给本文下结论——也许是认为“您在这里倡导的是一种专有设计,而不是集中于 BPEL!”毫无疑问,使用 BPEL 是以活动为中心的服务的“最容易”实现方法。但是今天的 BPEL 技术尚处于成熟周期的开头。它们还没有为处理某些类型的复杂、大规模应用程序(例如本文中的最低航班票价服务)做好准备。活动对象模型是可行的备选方法吗?我强烈地认为是这样,并且基于本文中介绍的此类模型的策略是用于在企业中和在企业之间连接支持 Web 服务的应用程序的关键,并具有一组支持对连接的应用程序之间的交互进行管理和监视的功能。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=332386
ArticleTitle=SOA 探索,第 2 部分: 使用活动对象模型解决复杂业务事务的挑战
publish-date=08212008