翻译自 http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html?page=1
在Spring中常常使用JTA以及XA协议来实现分布式事务,不过我们也有其他选项。最佳实现取决于你的应用场景,比如使用什么类型的资源,如何在性能、安全、可靠性和数据完整性之间权衡。在这个系列文章中,来自SpringSource的David Syer将详细讲解Spring应用程序可以使用的7种分布式事务模式,其中3种使用XA,4种不使用XA。
级别: 中级
Spring框架对JTA(Java事务应用接口即Java Transaction API)的支持,使得应用程序可以在没有运行J2EE容器的情况下使用分布式事务和XA协议。然而即便如此,XA对于管理员来说仍然是昂贵并且可能会是不可靠或者笨重的。它可能会带来初次体验的惊喜,但最终不少类型的应用都会避免使用它。
为了帮助你理解实现分布式事务的各种方法,我将分析7种分布式事务处理模式,提供具体的示范代码,我将按照安全性和可靠性由高到低的方式来逐个表述,从那些在最常用情况下能最大程度保证数据完整性和原子性的方法开始。当你依次往下阅读时,注意事项和使用限制会逐渐增多。另外这些模式基本上也是按照运行成本倒叙安排(从成本最高的开始)。这些模式都是架构或者技术模式,而不是业务模式, 所以我不会关注于具体业务,而只是提供说明各个模式如何工作的简短代码。
注意只有开始的3个模式使用了XA,而这些由于性能原因可能并不合适或不可接受。我并不会扩展讨论XA模式,因为XA已经在其他文章中被充分讨论,尽管我在第一个模式中提供了简单的示范代码。通过阅读这篇文章,你将学习到你能通过分布式事务做到什么,不能做什么以及何时如何避免使用XA - 以及什么情况下应该用XA。
分布式事务和原子性
分布式事务(distributed transaction) 指的是包含多个事务资源的事务。事务资源指的是比如和关系型数据库和消息中间件通讯的连接器。通常这样的资源提供一些API比如begin()
, rollback()
, commit()
. 在Java编程中, 事务资源通常显露为一个由底层平台提供的工厂产品:对于数据库而言,它是一个由DataSource产生的连接,或者Java Persistence API (JPA) EntityManager
;对于Java Message Service (JMS)而言,它是一个会话(Session)
.
典型的一个用例, JMS消息触发一个数据库更新。一个成功的交互序列如下所示:
- 开始一个消息事务
- 获取消息
- 开始数据库事务
- 更新数据库
- 提交数据库事务
- 提交消息事务
如果一个数据库错误比如在更新时遇到约束冲突,理想的交互序列看起来如下:
1. 开始消息事务
2. 接收消息
3. 开始数据库事务
4. 更新数据库, 失败!
5. 回滚数据库事务
6. 回滚消息事务
如上的例子中, 消息在最后的回滚动作发生后传回中间件,并在某个时刻被另外一个事务所接收。这通常是好事,否则你可能无法记录失败。(自动重试和异常处理的机制不在本文讨论范围)
上面两个程序流最重要的特征在于它们是原子性的, 形成单个逻辑事务,要么全部成功,要么全部失败。
但究竟怎么保证程序流和上面的两种情况类似呢?必须使用到一些事务资源之间的同步,这样如果一个提交,那么全部提交,反之亦然。由于包含了多个事务资源,所以事务是分布式的,如果不采取同步措施,那么一定不会是原子性的。分布式事务技术和概念上的困难均在于资源之间的同步(或缺少同步)。
下面讨论的前面的3个模式是基于XA协议的。由于这些模式被广泛讨论过,我不会涉及太多的细节。熟悉XA模式的读者可以直接跳到共享事务资源模式(Shared Transaction Resource pattern)。
使用两阶段提交方式的完整XA(Full XA with 2PC)
如果需要‘防弹级别’的保护比如事务需要从服务中断中恢复, 甚至包括服务器崩溃,那么完整XA(Full XA)是你唯一的选择。在这个例子中用来同步事务的共享资源是一个特殊的事务管理器,用来协调使用XA协议的进程的信息。在Java中,从开发者角度来看,协议是通过JTA UserTransaction来暴露的。
做为一个系统接口,XA的能力大部分开发者尚不了解。他们需要知道有这么一个XA,能做什么,有什么代价,如何使用事务资源。代价来自于两阶段提交协议two-phase commit (2PC) ,事务管理器使用该协议来确保所有的资源在事务结束前对处理结果达成一致。
如果是Spring应用,会使用Spring JtaTransactionManager和Spring声明式事务管理(declarative transaction management) 来隐藏底层同步的技术细节。使用XA或不用XA之间的区别仅在于配置工厂资源:数据源(DataSource)实例,和应用程序事务管理器。这篇文档包含一个示范应用(atomikos-db项目)演示了这个配置。数据源(DataSource)实例和事务管理器是仅有的XA-或JTA-相关元素。
想了解示范应用是如何工作的,可以运行com.springsource.open.db下面的单元测试用例。MultipleDataSourceTests类插入数据到两个数据源中,然后使用Spring整合支持功能回滚这个事务,如列表1中所示:
列表1. 事务回滚
- @Transactional
- @Test
- public void testInsertIntoTwoDataSources() throws Exception {
- int count = getJdbcTemplate().update(
- "INSERT into T_FOOS (id,name,foo_date) values (?,?,null)",