Spring分布式事务XA事务(两段提交2PC)实现

在系统的早期阶段,不需要分布式事务。随着应用程序数量的增加,数据同步成为一个重要问题。在数据同步方面,很多公司付出了很多维护同步系统的费用。由此,引入了称为XA(扩展体系结构)的两阶段提交协议。该协议为全局事务处理提供类似ACID的属性。在本文中,将尝试在Spring框架中解释XA事务和使用XA事务。

两阶段提交协议是分布式系统的原子承诺协议。该协议的名称意味着由两个阶段组成。第一个是提交请求阶段,其中事务管理器协调所有事务资源以便能提交或中止。在第二个阶段,也就是提交阶段,事务管理器决定根据每个事务资源的投票通过提交或中止来终止操作。接下来将介绍2PC协议的实现细节。



XA事务需要每个XA资源的全局事务ID和本地事务ID(xid)。每个XA资源都通过start(xid)方法登录到XA Manager。该方法告诉XA资源正在卷入事务(准备好进行操作)。之后,通过调用prepare(xid)方法实现了2PC协议的第一阶段。此方法从XA资源请求OK或ABORT投票。XA资源从每个XA资源获得投票后,如果XA资源发送ABORT,所有XA资源都会发送OK或决定执行回滚(xid),则XA Manager决定执行提交(xid)操作。最后,为每个XA资源调用end(xid)方法,告诉事务完成。

由于网络丢失,机器关机以及某些管理员的错误,故障可能随时发生。在XA交易中,我们将根据它们发生的阶段对这些故障进行分类。故障首先发生阶段可能是协议开始之前。这是一个简单的故障,系统不需要回滚或任何类型的操作。我们只是不做这个特定时刻的操作。准备(提交 - 请求)阶段可能也会发生故障,可以通过使用超时策略进行回滚来轻松处理。最后但并非最不重要的是由于不完整的回滚和回滚链中的任何问题而可能发生的提交阶段故障。在上述所有这些情况下,事务管理器尝试恢复问题。接下来我们将看到事务管理器如何尝试克服失败。

对于恢复,事务管理器调用每个XA资源的恢复方法。XA资源跟踪日志并尝试重建其最新状态。事务管理器调用必要的回滚操作,并完成任务。这个过程似乎是快乐的道路,但有很多异常情况,日志有问题,如被破坏。在这种情况下,事务管理器采用一些启发式解决问题。此外,恢复过程取决于在应用之前编写操作日志的预写日志。对于性能问题,这些日志是以自己的格式编写的(不使用任何序列化),如果可能,系统应该更好地对其进行批处理。接下来我们来讨论Spring框架的XA事务支持的有趣的部分。

Spring框架为开发Web和独立应用程序提供了广泛的环境。像其他提供的实用程序一样,Spring也支持XA事务。但是,这种支持并不是本地实现,而是需要hibernate支持,或者web容器提供XA事务管理的框架。Spring有JtaTransactionManager,它提供事务管理实用程序并隐藏细节。通过这种方式,我们可以为同时更新的多个DataSource进行事务管理。当涉及使用XA事务管理时,对XA事务的hibernate和web容器支持有很多文章,这里不需要提及。但是,单独提供XA事务的框架可能会令人抓狂。因此,这里介绍Bitronix事务管理器

Bitronix易于配置,同时为事务管理提供良好的支持。它不常用于独立应用程序,但下面尝试为独立应用程序配置如下:


<bean id="bitronixTMConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<!--Disabling Jmx avoids registering JMX Beans to any container-->
<property name=
"disableJmx" value="true" />
</bean>
<bean id=
"bitronixTM" factory-method="getTransactionManager" class="bitronix.tm.TransactionManagerServices" depends-on="bitronixTMConfig" destroy-method="shutdown"/>
<bean id=
"transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name=
"transactionManager" ref="bitronixTM" />
<property name=
"userTransaction" ref="bitronixTM" />
<property name=
"allowCustomIsolationLevels" value="true" />
</bean>

我们现在可以有多个数据源配置。每个数据源应具有唯一的唯一名称属性。以下配置是针对Oracle,其他数据库可以有不同的配置。对于任何其他细节,您可以查看Bitronix网站。


<bean id="xaDataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
<property name=
"uniqueName" value="xaDataSource" />
<property name=
"minPoolSize" value="1" />
<property name=
"maxPoolSize" value="4" />
<property name=
"testQuery" value="SELECT 1 FROM dual" />
<property name=
"driverProperties">
<props>
<prop key=
"URL">jdbc:oracle:thin:@10.6.86.24:1521:test</prop>
<prop key=
"user">test</prop>
<prop key=
"password">test</prop>
</props>
</property>
<property name=
"className" value="oracle.jdbc.xa.client.OracleXADataSource" />
<property name=
"allowLocalTransactions" value="true" />
</bean>

XA Transactions (2 Phase Commit): A Simple Guide -

[该贴被banq于2017-04-11 09:18修改过]