本文来自于 Rational Edge:本文是两部分系列文章中的第二篇,介绍了用于以在面向服务的体系架构上安全的,而且一致的方式管理分布式事务的事务协调服务的原型设计。
我的两部分系列文章中的第一篇文章讨论了在定义及执行面向服务的体系架构中的复杂业务事务时的固有难点。作为解决此难题的一个手段,我们引入了事务协调服务(Transaction
Coordination Service,TCS)的概念,原则上,它可以用于管理安全的且一致的分布式事务。此处是第二部分,我将介绍一个
TCS 的原型设计,包含 Java 开发的示例代码。此原型可以部署到任何 J2EE 容器中,并展示为 Web 服务。
此外,该原型阐明了一个用于由 XML 定义文件建立事务的潜在解决方案,用串行或并行的方式控制业务操作的执行和结束。我在最后以一个对可以实现来提供完整的(虽然很简单)基于服务的事务管理系统的附加功能的讨论作为结束。
事务协调服务框架
在分布式的、无状态,且透明的环境中的事务管理,以及作为面向服务的体系架构(Service-Oriented Architecture,SOA),呈现出明显的困难。SOA
底层的体系架构直接与确保关键业务操作完整性的需求是冲突的,该体系架构中,服务应该是无状态的,广大用户可以访问到的松耦合的业务操作。当事务广泛地分布在也许能或也许不能参与标准的、两阶段提交事务的商业伙伴系统时是尤其正确的。因此,必须找出某种方法来协调分布式的业务服务,以便请求者可以将多个不同的业务功能(也就是,Web
服务)组合成集成的业务操作。目前,有许多主机和微型计算机的事务处理和监控系统,例如,IBM Customer Information
Control System (CICS) 和 Microsoft Distributed Transaction
Coordinator (MDTC),并且事务管理包含于所有的认证的 J2EE 实现中,例如 WebSphere
Application Server。与下面给出的原型相比,这些方法中的原则差别与面向服务的体系架构的性质有关
—— 也就是,许多服务是“察觉不到事务的”。这需要一个稍微不同的分布式管理方法来确保全部事务的完整性。同样,这里介绍的原型表现出复合的事务,其中有许多并行的操作,经常异步发生,并且需要在长期的持续时间内维持状态的能力。
在第一部分中,我揭示了事务协调服务(TCS)的概念,它能够管理以下与面向服务的体系架构中的分布式事务管理相关的情况:
- 业务功能和工作流定义。这涉及根据服务调用和消息设计核心业务操作,业务功能表示需要自动化系统的部分业务操作。
- 资源管理。TCS 要求控制关键系统资源的分配和释放,包括数据库记录、通信套接字、安全密钥,等等。
- 安全。在服务请求者和每个所需的业务服务之间安全通信的能力。这包括对非验证环境(例如,无状态的 Web 服务)中的安全的需求
- 并发。TCS 必须控制对可修改记录的读/写访问,例如,客户订单,来防止竞争事务之间的数据坏损。
- 事务完整性。这涉及确保定义明确的业务事务工作流中的 ACID(原子性 - 一致性 - 独立性 - 持久性)属性。
- 事务嵌套。TCS 必须能够任意地嵌套多个事务,以便整个事务依赖于每个“子”事务的成功。
- 事务环境。这涉及从一个业务事务到另一个的传递信息,提供关键的连接信息(例如,与客户订单、工作订单,和营业量综述相关的“order-ID”)。
- 恢复,适度地从服务失败、网络错误,或其他防止业务事务成功执行的情况中恢复的能力,这包括重试失败事务的部分或全部的能力。
当我提出此原型时,心里面有许多目标。第一,我想为展示为有状态的 Web 服务的事务的创建、组织,和执行建立一个简单的基本框架。第二,我希望让定义文件简单且灵活,满足
Web 服务消费者变化的需求。第三,我想要提供一个工作原型,例举出许多事物协调服务的关键需求,例如,如何管理无状态的服务调用。图
1 中显示了整个系统架构,并且包含了四个重要的规则包:transaction、service、exception,和
utility。还包括的是 SOA Transaction Definition 文件,它定义了业务事务、环境映射,和每个服务消息中包含的数据。
TCS 包 如图 1 所说明的,组成 TCS 原型的类放入四个包中:transaction
包、service 包、utility 包,和 exception 包。
Transaction 包中包含所有事务管理类,包括四个表示核心事务处理的类:
- SOATransactionManager —— 负责事务执行和恢复的全部控制
- SOATransaction —— 负责每个 ManagedCommitter(以远程服务的形式实现)的控制,包括提交者之间传递的环境信息
- ManagedCommitter —— 为两阶段提交协议(例如,准备、执行、提交)定义事务控制语义的接口
- SOATransactionContext —— 提供一个 ManagedCommitter 和另一个之间的映射,来共享事务环境信息的通用类
图 1:原型事务协调服务(TCS)框架
另外,事务包包含三个枚举类:TransactionIsolation、TransactionStatus,和
TransactionType。它们用于指示执行的隔离级别(例如,"no phantom reads"
或 "ANOMALY_SERIALIZED")、操作的当前状态(例如,"PENDING-COMMIT"),和当前执行的事务类型(例如,"SERIALIZED"
或 "PARALLEL")。
Service 包包含一个类SOARemoteService,它实现了 ManagedCommitter 接口并控制与
SOA Transaction Definition 文件(参见图 2 的实例)中定义的远程服务交互的细节。
Utility 包包含 XML 解析器及用于构造基于 SOA Transaction Definition 文件(图
2)的事务管理器的事务构造器类。原型使用 SAX 解析器机制 1 和构造器模式来执行该任务。
图 2:示例 SOA Transaction Definition 文件
还有一个类,用来为 SOA Transaction Manager 和每个所含的 SOA Transaction
生成独特的标识符,用于跟踪事务重试和恢复。
最后,exception 包包含 SOAException 基类。它用于记录错误条件并触发事务重试和恢复逻辑。要使得框架简单,所有附加的
SOA 错误处理都将作为继承此基类的子类。
事务构造器如何工作
调用 loadTransaction()方法(在完成实现中,将作为一个 Web 服务)及一个格式明确的 SOA
Transaction Definition 文件来执行 TCS 的初始化。如图 3 所示,SOATransactionBuilder
类向 SOAXMLParser(SAX 解析器包装类)注册,作为内容处理器并且接受或解释 SAX 解析事件来构建SOATransactionManager使用的SOATransaction列表。每个SOATransaction由一个
SOARemoteService 对象(实现了ManagedCommitter 接口 2 )集合构成,依次由服务调用信息(例如,服务
URL)、必要数据输入消息,和数据映射信息构成。
图 3:SOA 事务构造器
图 4 说明了事务构造器进行的操作序列,包括初始服务调用,随后是 SAX 解析所提供的 XML 文档,以及构建
SOA 远程服务对象和 SOA 事务对象。
图 4:SOA Transaction Manager Builder 序列图
事务管理器
组成事务管理器,形成原型事务协调服务(TCS)核心的三个类是 SOARemoteService、SOATransaction,和
SOATransactionManager。图 5 中显示了这三个类之间的关系,以及支持类和枚举。SOATransactionManager
包含两列 SOATransactions,一个用于串行执行,一个用于并行执行。所有的串行事务是按顺序执行的,所以它是一个先进先执行的管理
3 。根据特性,并行操作可以按任何顺序发生,因此,不存在那种对并行事务集合执行的限制。在一定时刻,可能有多于一个
SOATransactionManager在执行,但如果 SOARemoteService 对象不能够支持并发控制,那么也许会导致并发问题。
如图 5 中所示,SOATransactionManager 负责建立事务边界("begin"
和 "end"),以及初始化对每个 SOATransaction 的执行级联。注意到,事务隔离级别是在事务管理器层定义的。这是要确保所有的事务都在同一个“场地”上执行,也就是说,事务的隔离不会比另一个高或低。
图 5:SOA Transaction Manager 类图
SOATransaction 类包含一个 ManagedCommitters(由SOARemoteService
类在原型中实现)的列表,它是按照 SOA Transaction Description 文件中定义的顺序执行。如图
6 所示,每个 SOATransaction 将在每个 ManagedCommitter 上执行准备、执行、提交/回滚,和结束步骤,利用图
5 中左边显示的 TransactionStatus 枚举类来跟踪事务的状态。如果任何 ManagedCommitters
报错,SOATransaction 将向集合中的每个对象发出回滚命令,确保整个事务的完整性。
按照必要的步骤执行 SOARemoteService,连接到已定义的服务,映射所有事务环境信息,生成输出消息,接收输入消息,并且通常管理远程服务调用的资源
4 。由于 SOARemoteService 对象需要实现 ManagedCommitter 接口,所以该调用的服务必须能够处理“回滚”命令(参见图
5 中 SOARemoteService 属性中的 transAware 布尔值)。在无状态服务的情况下,需要定义可以逆转变更的补偿服务调用。在此原型中,SOARemoteService
假定了远程服务提供了 Web Services Description Language (WSDL) 文件,来定义输入消息、输出消息,和绑定信息。
图 6:SOATransactionManager 序列图
实现细节
注意:我没有承诺任何关于原型代码的性能、准确性,或完整性的内容!其目的是为了帮助阐明 TCS 的实现细节,而不是创建一个产品级品质的组件。如果您
1) 对开始一个项目感兴趣,并且 2) 愿意将其变为通用的有用框架,我邀请您联系我。
原型 TCS 已经由 Java (5.0) 实现,以及 JUnit 测试用例、Apache Ant 构建文件,和实例配置文件。Eclipse
IDE 作为开发环境,并且所需要的库包含于 lib 目录中。实例使用了许多开源包,来生成 Universal Unique
Identifier (UUID) 5 、SOA Transaction Definition 文件的基于 Xerces
SAX 2.0 的解析 6 ,和标准 J2EE 运行时库(用于部署到 J2EE 容器中)。如果您在系统上安装了
Ant 的最新版本,那么所包含的 Ant 构建文件应该可以工作的很好 —— 但我不作任何保证!原型不包含作为 J2EE
会话 bean 来为中间部分的服务来部署所需要的代码,但其大部分是机械的操作, SOATransactionManager
生成本地/远程接口类,EJB (Enterprise JavaBeans) 描述符,和 JAR (Java Archive)
文件,并按 Web 服务部署此代码到一个合适的容器中(留给读者做练习 7 )。
两个单元测试 XMLParserTest 和 TransactionTest,是基于 JUnit 测试框架的。这两个测试获得所提供的示例
SOA 定义文件(CreateItineraryTransaction.xml),并分别输出分析操作结果,或事务执行。当然,由于所提供的服务不存在,所以
SOARemoteService 的实现仅仅对 execute() 操作返回真。
源代码包还包含由原型代码生成的 Javadoc 文件。这些提供了便捷的方式来观察完整的代码基础并且参考本文中我所包含的模型视图中没有充分说明的具体类信息。
那么遗漏了什么?
简单地说:许多。我有意地省去了好几个通常对产品级质量的服务很关键的条目,着重于事务管理工具的工作。但我没有忘记它们!因此在最后一部分,我将讨论所描述的框架能够/应该如何扩展,以支持
TCS 余下的特性。
安全性
明显的是,安全性是安全的事务管理服务中的关键部分。安全的 SOA 远程操作本身是一个非常复杂的问题,并直接影响所有
TCS 实现。然而,存在一些相对简单的方法来处理 TCS 和任意受管理服务之间的安全通道。第一个是利用协议,例如
SSH (Secure Shell) 或 HTTPS 来在 TCS 和所定义的服务提供者之间创建加密的套接字。这将帮助确保通道相当安全,而不受侵入。然而,这不能满足服务层的验证和授权的需求。由于大多数服务是无状态的,所以每次调用服务的时候就需要执行验证
—— 一个非常费时的过程。作为一个潜在的解决方案,安全标识可以作为初始SOARemoteService prepare()
调用的一部分被生成,在事务生命周期内对从服务提供者来的每个服务都是有效的。使用SOATransactionContext
可以将此安全性标识传递给ManagedCommitter 集合中的每个事务。另一种选择,安全证书可以在构造时发给
SOATransaction,可以对当前事务会话有效。最后,可以使用一个联合的方法,当服务提供者与其他提供者建立一个“信任环”时,例如如果
SOARemoteService 向一个提供者注册,所有其他的都认同此安全标识(本质上,“单点登陆”的形式)。
资源管理
就 TCS 而言,主要的资源管理问题涉及处理在事务操作过程中超时的服务。例如,考虑一个串行的事务,它发布了一个
commit() 给许多被执行的SOARemoteServices,然后延迟了,由于 TCS 服务器上的负载,发送剩下的
commit() 调用要相当长的时间。如果由 SOARemoteService调用的服务实现服务级超时 —— 并且它们应该,在事务管理器失败的情况下允许释放所持有的资源
—— 那么可能做到的是,对于 SOATransactionManager,在碰到已经超时之前向许多业务功能发布
commit() 调用,使事务处于不一致状态(例如 COMMIT_FAIL TransactionState,如图
5)。要防止此情况,事务超时应该总是比包含服务调用的 shortest 少,这定义在对每个服务的示例 SOA Transaction
Definition 文件中(参见图 2 中包含 ?service ... timeout="60"
.../?的代码行)。
并发控制
并发控制在业务事务术语中通常涉及生成和维护数据存储上的锁,例如关系数据库。在事务执行的过程中,这些锁确保对特定数据项的操作不会被其他同时发生的操作覆盖掉。然而,如上面提到的,对于资源管理,必要的是,任何使用并发控制的服务都能够在失败(例如,超时)的情况下最终释放那些锁,这是为了避免将数据置于意外的,或不一致的状态。此外,有许多不具备并发控制的服务,这也可能导致不一致的数据。由于这是一个服务层的功能,TCS
为确保并发而能做的非常少,除了查看是否有影响同样数据记录的并行事务,如果有,发布一个数据冲突可能出现的警告。
恢复/重试
事务失败,这是如同宇宙热寂一样不可避免的。因此,任何具有完整特性的 TCS 都应该有一个工具来存储失败的事务,用于随后的重试。有两种考虑的方法。第一种是“有保证的交付”事务,将事务发送给
TCS,并期待在整体过程中,直到成功之前都将重试。这要求将事务存储在持久的数据存储器中并安排未来的重试尝试。TCS
事务管理器随后会在所有要素都成功执行的时候获取并重试整个事务。事务恢复的另一个种类是“最佳努力”执行,在确定事务是失败的且发布回滚命令之前,事务将重试
ManagedCommitter 许多次。
嵌套
本文中所介绍的原型 TCS 没有实现任何形式的事务嵌套,在嵌套中,一个完整的事务是另一个事务的“子”事务,并且“父”事务依赖于所有孩子的成功执行。允许嵌套会导致内部事务成功,但父事务仍旧可以失败的情况
—— 那么您如何回滚先前提交了的子事务呢?
目前的实现提供一个服务,可以递归地调用 TCS 来建立其自己的事务。这样,如果初始的事务调用了 Service
X,它可以使用另一个 TCS 实例为 Service Y、Q,和 Z 建立一个事务。一旦此事务成功了,Service
X 向原有的 TCS 报告成功。此方法仍旧存在着问题,Service X 在原有 TCS 通知 Service
X 提交(换句话说,子事务在父亲完成前提交)之前已经提交了变更,但可能做到的是撰写 TCS,使其考虑到暂停/恢复一个事务来说明
Service X 的执行调用及其提交调用之间的延迟。如您从此简要讨论中看到的,嵌套通常是复杂、混乱,且危险的,所以通常最好尽可能避免它。
结束语
在本文中,我介绍了使用事务管理器控制一组事务的分布式事务协调服务的原型实现。事务包含一组受管理的,执行准备、执行,和提交/回滚事务操作的提交者。虽然,对于此组件还有许多余下的特性可以实现,但是我希望我为那些处理
SOA 系统来开发和管理分布式事务的架构师或设计人员提供了有用的框架。
分布式的,可交互服务的 SOA 方法对解决许多企业整合的困难有着诱人的承诺。服务可以呈现给消费者 —— 不论是组织内部或外部的
—— 使用相同的接口和连通协议。利用标准的协议(例如,XML),将数据在定义明确的、独立于平台的协议上传输。然而,许多问题目前仍旧阻碍了
SOA 方法的广泛采用,其中一个是复杂业务事务的建立和管理。我希望我的这个两部分的文章能够对您了解 SOA 的事务管理部分有所帮助,并提供一些有用的采用指导。
注释
1SAX(Simple API for XML)解析器是单向的 XML 阅读器,获得定义明确的 XML 文件并生成关键事件,例如标签、属性,和字符文本。XML
解析器的另一个形式是基于 DOM(Document Object Model)的,它形成了完整 XML 结构的内存中的对象模型。对于此实例,我选择更加简单且更加快速的
SAX 实现。
2ManagedCommitter 只实现了 OpenGroup(www.opengroup.org)为两阶段提交处理所定义的部分
XA 接口。这是为了避免说不清楚具有太多的细节的原型。在 TCS 的产品版本中实现完整的 X/Open 规范将是直接了当的。
3 更成熟的方案可以使用一个优先级分数来确定执行顺序,但原型的主要目标是简单明了。
4初始原型定义了 SOALocalService 和一个 SOARemoteService 类,显示出可以使用与远程服务完全一样的方式来调用本地资源,SOALocalService
对象从当前原型中略去了,为了强调协调远程服务的细节。
5 该类使用了 UUID 生成引擎(参见 Tatu Saloranta: http://jug.safehaus.org)的
JUG 实现。
6 http://xerces.apache.org
7 您不会讨厌作者这样做吧? |