为 OTN 撰稿
为 Oracle 技术网撰写技术文章,获得报酬的同时可提升技术技能。
了解更多信息
密切关注
OTN 架构师社区
OTN ArchBeat 博客 Facebook Twitter YouTube 随身播图标

了解服务补偿

作者:Jürgen Kress、Berthold Maier、Hajo Normann、Danilo Schmeidel、Guido Schmutz、Bernd Trops、Clemens Utschig-Utschig、Torsten Winterberg

了解面向服务的架构中的事务和补偿问题。

行业 SOA 文章系列的一部分

2013 年 9 月

简介

本文将介绍项目中成功应用的一些最重要的 SOA 设计模式,包括补偿模式和 UI 调解器模式、通用数据格式模式和数据访问模式。所有这些模式都包含在 Thomas Erl 的《SOA Design Patterns》[REF-1] 一书中,在此与我们的实践经验一起详细介绍。首先介绍“最佳”SOA 模式集中的补偿模式。

动机

SOA 中出现错误时需要补偿,因为一般不能将多个原子服务操作与典型事务联系在一起,这会违反松散耦合的原则。尤其是在编排中或通过应用组合模式将服务操作组合成流程或新服务,因此必须扩展事务框架 (Transaction Bracket) 时,将出现这种错误情况。我们需要机制来撤销各个服务的影响(整个系统的状态变化)并确保始终保持一致的系统状态,以便保持系统的完整性。

对于补偿模式,我们想讨论以下问题:

  • 为何补偿对 SOA 如此重要?
  • 补偿主题与事务主题有何联系?
  • 关于补偿存在哪些挑战?

具体问题

根据定义,服务是提供明确定义的业务功能的原子单元。如果在使用服务时出现功能错误或技术错误,服务负责处理这些错误,向服务调用方发送相应消息,并确保整个系统的完整性不受损害。

总有一些情况下单凭服务无法确保完整性。当多个服务与一个编排引擎互连形成一个流程或组合服务时,就是这种情况。如果其中一个服务出现错误,就必须撤销前面所有服务所做的更改。原则上来说,该服务操作或相连的事务协调器将不得不与编排中所涉及的所有服务操作通信并撤销处理步骤。如果运行时平台在编排过程中遇到错误,也适用相同的情况。在这种情况下,必须确保可以撤销前面调用的服务所做的更改。根据 ACID 原则(原子性、一致性、隔离性、持久性),解决这种问题的典型方法是使用事务协调器。

使用事务将意味着涉及的所有服务都能通过回滚撤销其更改。为此,在整个处理期间,一般来说必须阻塞所用后端资源(如数据库记录或其他数据资源)。这通常导致并行访问被阻塞,特别是在涉及运行时间非常长的流程时,IT 资源可能会被阻塞数周时间。

但在 SOA 中,服务是松散耦合的 [REF-2]。事务代表围绕一系列服务的逻辑框架,因此服务之间存在依赖关系,这是 SOA 中不希望出现的情况。因此,需要替代解决方法:


解决方法

如果在调用服务时,流程中出现功能错误或技术错误,那么补偿模式机制可将整个系统恢复到一致的状态。

ind-soa-service-comp-fig01
图 1:服务将不得不锁定修改过的 IT 资源以便后续执行回滚 [REF-1]

解决方案

补偿可以设计为“逻辑撤销”。对于修改功能,必须提供对应的撤销逻辑。具体来说,这意味着导致系统状态更改的每项服务操作(如“创建订单”)需要一个对应的撤销服务操作,可将其状态重置到以前的状态。

这种撤销服务操作通常并非简单的反向服务操作,比如“删除订单”,即从数据库表删除一个条目。通常必须撤销的服务操作结果要比这复杂,且过程必须是可记录的。这对应于业务领域中一个长期实践的常见过程。并不是“根除”错误的发布,而是通过取消发布将其撤销,并记录对应的操作。

服务编排和组合需要这种补偿的概念。一种可能出现的情况是,在一个流程序列中已成功执行了各种服务操作。随后调用的某个服务操作调用了一个错误,这就需要撤销先前那些属于同一服务上下文且已更改系统状态的服务操作。

在此,不是使用一个技术事务来确保系统的完整性,而是向服务编排添加一个“补偿处理程序”功能。“补偿处理程序”可用于定义发生错误时将调用的撤销服务操作。

通过要求每个服务或流程提供一个故障操作,可在链中实现向后传播错误。同步服务在遇到错误时不存在这个问题,它们会返回一个对应的故障。但对于所有异步服务调用,必须找到一个替代解决方案。一种可能是在服务编排中实施异步请求-响应模式。在这种情况下,调用进程会一直等待,直到发出反馈。如果发生错误,被调用的服务将向调用方发送一条错误消息,而不是“正常的”反馈,由此激活对应的补偿处理程序,执行必要的逻辑回滚步骤。在 BPEL 中,将使用选取活动而不是接收活动,以便对响应或错误做出反应。在 BPMN 中,可以使用基于事件的网关描述来自异步服务调用的不同响应。

凭直觉,用于处理补偿的 BPEL/BPMN 语言结构应适用此种情况。在异常处理程序中,发生的错误被“捕获”,并根据需要使用“补偿”活动,触发对应的补偿处理程序以执行逻辑回滚 [REF-4]。

如过没有事务监视器,将无法锁定相应的数据源,因此,功能流程通常使用属性,如“orderstatus=RELEASED”。如果发生错误,还必须对这些属性进行响应的操作。

ind-soa-service-comp-fig02
图 2:补偿并不需要阻塞任何底层 IT 资源 [REF-1]

应用领域

如果服务组合中有多个服务操作被调用,且彼此独立地更改了整个系统的状态,这种情况下可以使用补偿。如果服务隐式地更改数据(如通过激活数据库触发器),则不能使用补偿。

通过 Web 服务封装“原有系统”并以服务操作形式提供功能时,理所当然使用补偿模式,因为这种情况下极少考虑使用事务管理器。通常情况下,唯一的选择是在发生错误时通过补偿来重置服务操作,因为原有系统不提供通过回滚恢复原始状态的可能性。

影响

使用补偿时请牢记以下影响:

  • 在现有服务的技术性和代码中必须考虑撤销服务操作的结构。
  • 补偿迫使开发人员改变思维方式、放弃典型事务模型。

补偿逻辑并非如数据回滚中那样自动可供使用,而是完全由服务设计人员处理,且必须在设计中纳入考虑。由于事关整个系统的完整性,此任务特别重要,最终将直接影响服务质量。

例如,如果撤销服务操作的构造方式使其不加区别地重置一切而不考虑业务逻辑,这可能导致以为已经干净地执行补偿,而实际上整个系统不再处于一致状态。如果组合的设计人员没有被调用服务或其撤销服务操作的详细逻辑描述,很可能发生这种情况。

在补偿阶段,所有服务调用将由对应的撤销服务操作按相反的顺序撤销。在某些情况下,会遇到这种设计的限制,如当服务调用已经被流程序列中的条件逻辑拒绝时。例如,规则服务确定操作在调用时能否正常执行。在设计时,补偿处理程序不知道应调用哪些撤销操作。

这种情况下可以考虑两种使用模式。理想情况下,服务(特别是撤销服务)是以幂等方式实现的。在流程中调用所有撤销服务操作除了对系统负载有影响外,没有任何影响。另外,必须在单独变量中记录所有调用。如果使用 BPEL 或 BPMN 流程引擎,变量将持久保存并保持对后续激活/回滚可用。如果没有流程引擎,或者流程仅实现“触发即忘”消息交换模式,那么在消息的单独消息头区域记录所有调用是一种流行的但没什么吸引力的解决方案,它使用单独的补偿流程撤销调用。

然而,还有一些情况和用例中执行事务才是最重要的。这种情况的典型示例是银行转账,从业务流程的角度来说,使用补偿作为基础的是至关重要的。在这种情况下,SOA 的松散耦合原则 [REF-2] 与完整性的概念发生冲突。

可以针对这些情况开发替代解决方案,上面已经介绍了两种。

事务封装

如果有令人信服的原因不能使用补偿模式,可以将流程转换为“组合服务”[REF-1]。这将扩展服务 (transferMoneyFromAccountToAccount(…)) 的功能范围并在此服务内处理事务。

一种替代方案是使用某些 SOA 套件自带的专用机制,这些机制允许在 ESB 和 BPEL 集成流程的服务调用之间管理事务上下文。这就可以在出现错误时通过回滚撤销组合事务,而不必提供对应的撤销操作。

总的来说,使用典型事务框架将在服务操作(发出、接收)之间产生依赖性,从而人为地将技术接口协议扩展为包括事务框架的细节。这将绕过某些 SOA 原则,如重用或松散耦合。

使用现有事务协议

与将事务责任转移到服务操作不同,Thomas Erl 考虑的是使用事务协调器,如 WS-TX 或 XA [REF-5]。对于服务,这意味着一旦操作执行即可向事务协调器注册服务,从而将服务的事务管理责任委托出去。

由于在更长的时间内不得阻塞 IT 资源,因此只在临时事务中建议采用此实现,高性能集成流程中也经常会发现这种情形。

ind-soa-service-comp-fig03
图 3:事务协调器管理事务限制 [REF-1]。

总结

讨论 SOA 或尝试初步实施时,往往将故障处理话题放在一边,重点关注“顺利执行的路径”。但是,因为需要对架构进行一些改写,所以不可避免地要讨论故障处理的概念。每位架构师都必须处理 SOA 内的事务和补偿问题,这样才能评估影响。

还可以使用事务转移到服务实现或使用分布式事务等替代办法。但这些替代办法与 SOA 的基本原则相抵触,尤其是松散耦合原则,因此应避免使用。

必须在服务设计阶段尽早考虑补偿。服务和流程设计人员必须定义撤销服务操作的范围。无论是封装原有应用程序的服务还是新开发的服务,都要这样做。

市场上许多流程引擎提供了符合标准的补偿处理程序,支持强健的系统安装,即使是在复杂的企业系统环境中。

要点

  • 必须在服务设计阶段尽早考虑补偿,从而考虑到撤销操作。
  • 仅使用“顺利执行的路径”执行的系统测试并不包括重要的 SOA 架构模块。
  • 补偿模式的实现需要明确的设计,避免系统资源依赖,尽量采用原子和独立服务操作。
  • 如果要求 100% 的事务安全性(包括恢复)并且编排是临时的,有一个有效、常用的替代办法:使用支持 XA 并执行资源锁定的事务管理器。

参考资料

[REF-1] Thomas Erl。“SOA Design Patterns”。http://www.soapatterns.com/

[REF-2] Berthold Maier、Hajo Normann、Bernd Trops、Clemens Utschig-Utschig、Torsten Winterberg。“松散耦合”。Java Magazin,2009 年 3 月

[REF-3] OASIS 标准:Web 服务业务流程执行语言 2.0 版。“补偿处理程序”: http://docs.oasis-open.org/wsbpel/2.0/OS/wsbpel-v2.0-OS.html#_Toc164738526

[REF-4] 跨服务事务: http://www.servicetechspecs.com/ws-atomictransaction

[REF-5] OMG。“业务流程模型和标准 (BPMN) 2.0 版”: http://www.omg.org/spec/BPMN/2.0