如何实现XA式、非XA式Spring分布式事务

Spring应用的几种事务处理机制html

 

Java Transaction API和XA协议是Spring经常使用的分布式事务机制,不过你能够选择选择其余的实现方式。理想的实现取决于你的应用程序使用何种资源,你愿意在性能、安全、系统稳健性、数据完整方面作出何种权衡。在此次JavaWorld大会上,来自SpringSource的David Syer跟你们分享了Spring应用的几种事务处理机制、三种XA式、四种非XA式事务协议。spring

 

Spring框架支持Java Transaction API(JTA),这样应用就能够脱离Java EE容器,转而利用分布式事务以及XA协议。然而即便有这样的支持,XA开销是昂贵的,不稳定并且笨重不利于管理,不过一些其余的应用能够避免使用XA协议。数据库

 

为了让你们对所涉及的几种分布式事务有所了解,我会分析七种事务处理模式,并 给出具体代码实现。而且从安全或者稳定性入手倒序展现,能够看看从安全、稳定性出发,如何在通常场景下,保障数据高完整性和原子性。固然随着话题的深刻, 更多的说明以及限制就会出现。模式也能够从运行时开销倒序展现。考虑到全部模式都是结构化或者学术性的,这一点有别于业务模型,所以我不打算展开业务用例 分析,仅仅关注每种模式其少部分代码如何工做的。编程

 

尽管只有起初的三种模式涉及到 XA协议,不过从性能角度出发,这些模式或许没法知足需求。考虑到这些模式无处不在,我不想作过多地扩展,只是对第一种模式作一个简单的展现。读完此文,你能够了解能够用分布式事务作些什么、不能作什么以及如何、什么时候避免使用XA,什么时候必须使用。安全

 

分布式事务以及原子性服务器

 

分布式事务涉及不止一个事务资源。好比,在关系数据库和消息中间件之间通讯的链接器,一般这些资源拥有相似begin()、rollback()、commit()的API。在此,一个事务资源一般是一个工厂产品,这个工厂一般由底层平台提供:以数据库为例,DataSource提供Connection,或者Java Persistence API(JPA)的EntityManager接口;又如Java Message Service(JMS)提供的Session。session

 

一个典型的例子,一个JMS消息触发一次数据库更新。此过程能够分解成一时间线,一个成功的交互顺序是下面这样:架构

 

  1. 开启消息事务并发

  2. 接受消息框架

  3. 开启数据库事务

  4. 更新数据库

  5. 提交数据库事务

  6. 提交消息事务

 

若是数据库出错,好比更新时出现诸如违反约束的问题,一个理想的顺序应该是下面这个样子:

 

  1. 开启消息事务

  2. 接受消息

  3. 开启数据库事务

  4. 更新数据库失败

  5. 回滚数据库事务

  6. 回滚消息事务

 

在这个案例中,最后的回滚发生后消息返回给中间件,而且在某种程度返回的消息会被其余事务所接收。一般这是件好事,可能你并无对失败作记录。自动重试处理异常机制超出了本文的范畴。

 

以上两种时间线中最重要的特性是它们的原子性,造成一个单一的逻辑事务单元,要么都成功要么都失败。

 

那么用什么确保时间线会的顺序呢?事务资源之间必须保持某种同步,一旦对某个数据源作提交,要么都提交了,要么都回滚。否者整个事务就不缺少原子性。之因此是分布式事务,是由于有多个数据源,没有同步就没有原子性。分布式事务技术和概念的核心问题都是围绕资源的同步或者没法同步展开的。

 

前三种模式的如下讨论都是基于XA协议,考虑到这三种模式分布普遍,本文不会涉及太多的细节,假若你熟悉XA模式或许愿意直接跳到共享事务资源模式。

 

二阶段提交完整XA协议

 

若是你须要近乎完美的防御 (close-to-bulletproof)确保你的应用事务在断电后恢复以及服务器崩溃,完整XA是不二之选。共享资源一般须要作事务同步,在此状况下,它是一个采用XA协议协调处理过程的信息特殊的事务管理器。在Java领域,从开发者的角度看,这个协议是经过JPA UserTransaction暴露给你们。

 

基于系统接口,XA做为一种促成科技(enabling technology)对多数开发人员不可见,所以他们须要知道XA在哪、促成什么、耗损如何以及如何利用事务资源。事务管理器采用二阶段提交(2PC)协议,在确保事务结束前全部资源采用同一个事务结果的同时,也会带来性能耗损。

 

如 果是Spring促成的(Spring-enabled),应用会采用Spring的JtaTransactionManager以及Spring声明式 事务管理,这样会隐藏到了底层事务同步的具体细节。对于开发人员用没用XA的差异就在于对工厂资源的配置:DataSource实例,以及应用的事务管理 器。本文会经过一个应用案例(atomikos-db项目)来揭示这个配置,数据库实例和事务管理器仅是XA或者JTA特定的应用元素。

 

为了揭示此案例如何工做,在com.springsource.open.db.下运行这个单元测试。一个简单的 MulipleDataSourceTests类仅是将数据插入两个数据源中,而且采用Spring整合支持的特性对事务进行回滚,代码见清单1:

 

清单一、事务回滚

 

@Transactional

@Test

public void testInsertIntoTwoDataSources() throws Exception {

 

int count = getJdbcTemplate().update(

"INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0,

"foo");

assertEquals(1, count);

 

count = getOtherJdbcTemplate()

.update(

"INSERT into T_AUDITS (id,operation,name,audit_date) values (?,?,?,?)",

0, "INSERT", "foo", new Date());

assertEquals(1, count);

 

// Changes will roll back after this method exits

 

}

 

接着验证这两个操做是否同时回滚,代码清单如清单2:

 

清单二、回滚验证

 

@AfterTransaction

public void checkPostConditions() {

 

int count = getJdbcTemplate().queryForInt("select count(*) from T_FOOS");

// This change was rolled back by the test framework

assertEquals(0, count);

 

count = getOtherJdbcTemplate().queryForInt("select count(*) from T_AUDITS");

// This rolled back as well because of the XA

assertEquals(0, count);

 

}

 

更进一步理解Spring事务管理如何工做以及如何配置,请参看Spring参考指南。

 

一阶段提交优化XA协议

 

许多事务管理器采用这种优化模式,能够避免单一事务资源下的2PC过分开销,你的应用服务器最好可以判别此种状况。

 

协议和最终资源策略

 

多数XA事务管理器另外一个特性是,不管是单一XA兼 容资源仍是全部资源都XA兼容,事务管理器均能提供相同的恢复保障。它们是经过给资源排序,而且给非XA资源投票实现,假若事务提交失败,全部其余的资源 都能回滚。事务有近乎百分百的保障,但缺点是,假若事务失败,此时不会留下太多信息。换言之,若是要获取这些信息,须要作一些额外的步骤,好比在一些高级实现。

 

共享事务资源模式

 

这个模式不错,系统全部的事务资源由一个相同的资源提供支持进而移除XA,下降系统的复杂度,提升吞吐量。固然不能拿来处理全部的用例,但倒是如XA般坚固,并且处理速度更加的快。共享事务资源模式做为一种保障存在与特定的平台和处理场景中。

 

一个简单熟悉的例子就是共享一个数据库的Connection,它存在于一个对象关系模型(ORM)控件和一个JDBC控件之间。Spring事务管理器就是如此,它支持ORM工具,好比Hibernate、EclipseLink以及Java Persistence API(JPA)。相同的事务能安全的跨越ORM和JDBC控件之间,一般此事务是由service层受事务控制的执行方法所驱动的。

 

此模式的另一个特色是,消息驱动的单个数据库更新,如本文初始阶段的简单例子。消 息中间件系统须要存储这些数据,一般是关系型数据库。实现这种模式,须要将消息系统指定到相同的用于存储业务数据的数据库中。这种模式依赖消息中间件供应 商所提供的存储策略细节,以便可以将消息中间件配置在相同的数据库中,并嵌入相同的事务处理。

 

不是全部的供应商都提供了此种模式,不过一种可替代,几乎能够用于任何数据库的方式,即利用Apache ActiveMQ的传递消息,而且插入一个存储策略进入消息代理中。一旦你知道了其中的诀窍,配置起来很容易的,我会在本文的shared-jms-db 项目案例中演示。此模式的所用的代码无需关注,它们会在Spring配置中获得声明。

 

名为 SynchronousMessageTriggerAndRollbackTests 的案例中,单元测试校验全部与同步消息接收者相关的讯息。 testReceiveMessageUpdateDatabase方法接受两个消息,接着利用消息插入两条记录到数据库中。若是此方法退出,测试框架回 滚事务,这样就能校验消息和数据库更新是否发生回滚,如清单3所示:

 

清单三、验证消息和数据库更新是否回滚

 

@AfterTransaction

public void checkPostConditions() {

 

assertEquals(0, SimpleJdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS"));

List<String> list = getMessages();

assertEquals(2, list.size());

 

}

 

配置文件最重要的部分就是ActiveMQ的持久化策略,链接消息系统和相同数据源做为业务数据,Spring JmsTemplate的flag标签用来接收消息。清单4 展现了如何配置ActiveMQ持久化策略:

 

清单四、ActiveMQ持久化配置

 

<bean id="connectionFactory" depends-on="brokerService">

<property name="brokerURL" value="vm://localhost?async=false" />

</bean>

 

<bean id="brokerService" init-method="start" destroy-method="stop">

<property name="persistenceAdapter">

<bean>

<property name="dataSource">

<bean>

<property name="targetDataSource" ref="dataSource"/>

<property name="jmsTemplate" ref="jmsTemplate"/>

</bean>

</property>

<property name="createTablesOnStartup" value="true" />

</bean>

</property>

</bean>

 

清单5展现了Spring JmsTemplate中用来接收消息的flag标签:

 

清单五、设置JmsTemplate事务应用

 

<bean id="jmsTemplate">

<!-- This is important... -->

<property name="sessionTransacted" value="true" />

</bean>

 

若 sessionTransacted不为true,JMS session transaction API就没法被调用,消息接受者将没法回滚。最为重要的是,嵌入的代理包含一个特殊的async=false参数以及DataSource外包类,这样就能够确保ActiveMQ拥有同Spring同样的事务JDBC Connection。

 

一个共享数据库源能够由独立的单个数据源组成,特别是这些数据源拥有一样的RDBMS平台。企业级数 据库供应商均提供同名概念(the notion of synonyms)支持,表能够做为一个同名(synonyms)声明于多个schema中。借助这个手段,分布在不一样物理平台上的数据,能够都可 JDBC client赞成Connection事务管理。好比在一个真实的系统中,采用ActiveMQ共享资源模式实现,一般须要为消息和业务数据建立同名。

 

性能和JDBCPersistenceAdapter

 

ActiveMQ 社区的一些开发人员表示JDBCPersistenceAdapter会引起性能问题。然而,许多项目和上线系统采用ActiveMQ与关系型数据库搭配使 用。在这些案例中,公认的是日志版本的适配器能够用来提供性能,固然这对共享资源模式来讲是不利的,由于日志自己就是一个新的事务资源。然而,对于 JDBCPersistenceAdapter你们众说纷纭,见解不一。确实,有理由相信采用共享资源或许能提升日志案例的性能。这个一结论来自 Sping以及AtiveMQ工程师团队的研究。

 

非消息场景(多个数据库)的另外一种共享资源技术就是,在RDBMS平台一级利用Oracle数据库连接一个特征到两个数据库schema中。或许这须要改变应用代码,或者建立同名,由于表的别名会指向一个已连接数据库,此数据库包含连接的名称。

 

最大努力一次提交模式

 

开发人员必须知道,最大努力一次提交模式应用至关的广泛,可是在一些场景并不适用。这是一种非XA模式,它包含一 个同步大量资源单一相提交(single-phase commit)。参与者应该意识到这种折中,若是不用两阶段提交,那最大努力一次提交模式的安全性不如XA事务但也是至关不错。许多海量数据、大吞吐量事 务处理系统用最大努力一次提交模式提升性能。

 

最基本的理念就是在单一事务中尽量的延迟全部资源的提交,这样惟一可能发生错误的就是基础组件,而非业务处理错误。 采用最大努力一次提交模式的系统假定基础组件出错的可能性很是小,所以可以承受风险得到较高的吞吐量收益。若是业务处理服务也被设计为一个幕等式 (idempotent),发生错误的可能性也很小。(译者注:幕等式是数学和计算机科学特定运算的一个特性,应用初始化之后屡次操做其结果都不会再发生改变)

 

为了帮助你们更好的理解这个模式分析失败结果,我会用消息驱动数据库更新做为例子来加以说明。

 

本事务中两个资源将被统计、而且计算。消息事务在一个数据库以前开启,而后逆序结束。成功的顺序或许和本文开始的时候如出一辙:

 

  1. 开启消息事务

  2. 接受消息

  3. 开启数据库事务

  4. 更新数据库

  5. 提交数据库事务

  6. 提交消息事务

 

准确的说,此顺序前四个步骤都不重要,重要的是消息必须在数据库更新以前被接受,而且每一个事务必须在对应的资源被调用以前开启,所以合理的顺序应该以下:

 

  1. 开启消息事务

  2. 开启数据库事务

  3. 接受消息

  4. 更新数据库

  5. 提交数据库事务

  6. 提交消息事务

 

关键点是最后两步很重要,它们必须放在最后按顺序执行。按序之因此重要,其自己就是一个技术问题,不过这个顺序是有业务需 要决定的。这个顺序告诉开发者,其中的一个事务资源是特殊的,这个资源包含了如何在其余资源上运做的指令。这是一个业务顺序:系统不能自动告知走事务到哪一步了。即便消息和数据库是两个资源,事务也经常遵循这一流程。按序之因此重要,是由于事务必须处理失败案例。迄今最多见的失败案例是,诸如坏数据、编程错误等失败的业务处理。本例中,这两个事务很容易用来相应一个异常而且回滚。这样,业务数据的完成性获得保障,时间线与本文开始列出的理想失败案例神似。

 

引起回滚机制的精确性不是很重要的,这样的机制有好一些。最重要的是,提交或者回滚必须按照资源中业务顺序的逆序发生。在一个应用案例中,消息事务必须在最后提交,由于处理业务的指令包含在这个资源中,这是由于,不多有失败案例 其第一次提交成功,第二次失败。在这个点上,全部设计业务处理的部分都已完成,那惟一能引发部分失败的因素,可能消息中间件的基础问题。

 

注意若是 数据库资源提交失败,那么事务最终会发生回滚。因此是说非原子性失败模型(failure mode)其第一个事务会提交,第二个事务发生回滚。一般,事务中有n个资源,那么就存在n-1个这样的失败模型,这会致使一个子事务回滚后,其它一些资源处在提交后的不一致状态。在消息数据库用例中,失败模型的结局是,消息会回滚,而后是其余的事务,即便其余这些事务都经成功处理;能够判定最糟糕的事情 就是重复消息(duplicate message)被传递过来。什么是重复消息呢?一般状况下,事务中的早期资源被认为是包含有后续资源处理流程的讯息,所以失败模型的结果能够被认为就是重复消息。

 

一些富有冒险精神的人认为重复消息发生的可能性微乎其微,所以懒得去预测这些消息。然而,为了更加确信业务数据是准确性和一致性,仍是须要在业务逻辑层面对 此有清晰地认识。 若是怀疑重复消息可能发生,那么必须核实,业务处理过程是否处理过数据,在处理数据以前是否什么都没作。这个特定的说明有时指幕等业务服务模型 (Idempotent Business Service pattern)。

 

相关案例包括两个采用此模型的同步事务资源例子,我会在后面作一一分析,以及一些其它选项。

 

Spring和消息驱动POJO

 

案例best-jms-db项目中的代码,开发人员采用主流配置,这样就可使用最大努力一次提交模式。具体的作法是这样的,经过一个异步的监听器将消息传给一个队列,并将此数据插入 数据库表中。

 

TransactionAwareConnectionFactoryProxy 是Spring的一个存储控件,应用于这个模式中,也是最关键的组成部分。放弃采用供应商提供的粗颗粒度的 ConnectionFactory,configuration采用装饰模式包装了一个ConnectionFactory,用它来处理事务同步问题。 具体配置见jms-context.xml,以下清单6所示:

 

清单六、配置一个TransactionAwareConnectionFactoryProxy去包装一个供应商提供的JMS ConnectionFactory

 

<bean id="connectionFactory">

<property name="targetConnectionFactory">

<bean depends-on="brokerService">

<property name="brokerURL" value="vm://localhost"/>

</bean>

</property>

<property name="synchedLocalTransactionAllowed" value="true" />

</bean>

 

ConnectionFactory 无需知道哪一个事务管理器与其同步,每一时刻仅有一个事务处在活动(active)状态。这些是由Spring内部在处理。事务驱动是由配置在data- source-context.xml中的DataSourceTransactionManager完成的,事务管理器必须由轮询和接受消息的JMS监听器容器监控。

 

<jms:listener-container transaction-manager="transactionManager" >

<jms:listener destination="async" ref="fooHandler" method="handle"/>

</jms:listener-container>

 

fooHandler和方法会告知监听器容器某个具体的控件的具体方法获得调用,当一个消息达到”异步”队列。handler是如此实现的,接受一个String做为参数消息,并将其做为数据插入记录中。

 

public void handle(String msg) {

 

jdbcTemplate.update(

"INSERT INTO T_FOOS (ID, name, foo_date) values (?, ?,?)", count.getAndIncrement(), msg, new Date());

 

}

 

为了模拟失败,代码用一个FailureSimulator切面,它会检查消息内容是否真的失败;如清单7所示,在FooHandler在事务结束以前,处理消息以后,maybeFail()方法获得调用,因此它能影响事务的结果。

 

清单七、maybeFail()方法

 

@AfterReturning("execution( ..*Handler+.handle(String)) && args(msg)")

public void maybeFail(String msg) {

if (msg.contains("fail")) {

if (msg.contains("partial")) {

simulateMessageSystemFailure();

} else {

simulateBusinessProcessingFailure();

}

}

}

 

simulateBusinessProcessingFailure() 方法会抛出DataAccessException,就像数据库访问真的失败。一旦这个方法被调用,最理想的结局是全部的数据库以及消息事务都能回滚。 这个场景在案例项目AsynchronousMessageTriggerAndRollbackTests单元测试获得测试。

 

simulateMessageSystemFailure() 方法经过破坏底层的JMS Session来模拟消息系统失败。预期的结果是事务部分提交:数据库提交了,但消息回滚。这个在 synchronousMessageTriggerAndPartialRollbackTests单元测试验证过。

 

一样,在AsynchronousMessageTriggerSunnyDayTests类中,包含一个全部事务成功提交的单元测试。

 

相 同的JMS配置,相同的业务逻辑一样能够用在同步的环境中,消息由存储在业务逻辑中的阻塞请求所接收,而非监听器容器。此方法在best-jms-db案 例项目中获得展现。 sunny-day case以及事务所有回滚分别在SynchronousMessageTriggerSunnyDayTests和 SynchronousMessageTriggerAndRollbackTests获得测试。

 

链式事务管理器

 

在其余的最大努力一阶段提交模式案例中,一个粗糙的事务管理器实现仅仅是将一系列其余的事务管理器连接在一块儿,去实现事务同步。假若业务处理成功,全部的事务将会提交, 不然它们都能回滚。

 

ChainedTransactionManager 接受一系列其余的事务管理器做为注入属性,如清单8所示:

 

清单八、配置

 

<bean id="transactionManager">

<property name="transactionManagers">

<list>

<bean>

<property name="dataSource" ref="dataSource" />

</bean>

<bean>

<property name="dataSource" ref="otherDataSource" />

</bean>

</list>

</property>

</bean>

 

此配置简单的测试,仅是同时插入数据到两个数据库,回滚,同时确保两个运行回滚到最初状态。此实现做为存在MulipleDataSourceTests中的一个单元测试,如同XA案例中的 atomikos-db项目。假若回滚没有同步,有事务提交了,那测试就算失败。

 

记住,资源顺序很重要,它们是嵌套的,提交或者回滚以它们参与的相反顺序进行。其中一个事务最为特别:若是存在问题,最重要的事务会回滚,即使是这问题是一个资源失败。 一样,testInsertWithCheckForDuplicates()测试方法展现了幕等式业务处理如何从部分失败中保护系统,此实现做为一个里层资源(otherDataSource)中业务运算防护检测。

 

int count = otherJdbcTemplate.update("UPDATE T_AUDITS ... WHERE id=, ...?");

if (count == 0) { count = otherJdbcTemplate.update("INSERT into T_AUDITS ...", ...); }

 

update首先尝试和一个where子句执行,不出意外,update中的数据会插入数据库中。本例中的幕等式处理一个额外的花销是sunny-day case中额外的查询,这个额外的花销在复杂的每一个事务执行多个查询的业务处理中微乎其微。

 

其余选择

 

案例中的ChainedTransactionManager拥有简洁优点,并且扩展优化已作的很好。另外一个方式是, 当第二个资源加入时,利用Spring的TransactionSychronization API给当前事务 注册一个回调函数,此方式在best-jms-db案例中,最大的特色是TransactionAwareConnectionFactory和一个 DataSourceTransactionManager的结合。利用TransactionSynchronizationManager,这个特殊的案例能够扩展而且泛化到包含non-JMS的资源中。这样理论上有个优点,就是只有加入事务的资源获得支持,而非链上的 全部资源。然而配置依旧须要监听某个潜在的事务与之对应的资源。

 

一样,Spring工程师团队考虑将最大努力一阶段提交事务管理器特性做为Spring核心。你能够在JJRA issue中投票,若是你喜欢这种模式,但愿Spring中显示以及更加透明地支持此种模式。

 

非事务访问模式

 

非事务访问模式须要一个特殊的业务处理,这样才有意义。理想的状态是有时,其中一些你须要访问的资源边缘化,一点都不须要事务。好比,或许你须要将一行数据插入一个 审核表中,此操做是独立的,和业务事务是否成功无关。仅仅记录试图作了某事。更广泛的场景,人们高估了他们须要对其中一个资源作读写的频次,事实上,只有 访问就很好了。 不然,写操做须要获得很好地控制,所以若是发生任何错误,写操做能够被记录下来或者忽略。

 

在以上的案例中资源恰当地原理全局事务,但仍然有其本身的本地事务,本地事务无需与其余发生的事情保持同步。若是你使用的是Spring,主要的事务由一个PlatformTransactionManager驱动, 边缘资源或许是一个数据库Connection,它来自一个不受事务管理器控制的DataSource。每一次访问边缘资源须要将缺省环境设为 autoCommit=true。updates对读操做不可见,前者能够与其余非提交事务并发进行,但写操做带来的影响一般对其余操做来讲是当即可见的。

 

这个模式须要更多精细地分析,以及更多自信去涉及业务处理,但它同最大努力一阶段提交没 什么区别。当任何事情出错,一个通用的补偿事务服务对多数项目来讲太过庞大。不过简单的用例所涉及的服务,它是幕等式的仅仅执行一个写的操做,这种现象再 普通不过了。这些是非事务策略的理想场景。

 

Wing-and-a-Prayer:一种反模式

 

最后一种模式是一种反模式,它出现这样一个场景中,开发者不理解或者没有意识到他们已经存在一个分布式事务时。无需显示的调用底层的资源事务API,你不确 定全部的资源是否在一个事务中。假若用的是一个Spring事务管理器而非JtaTransactionManager,此管理器会将一个事务资源加入其中。这个事务管理器将会拦截Spring声明事务管理特性的执行方法,好比@Transactional;其余的资源不会注册到相同的事务中。一般的结局 是任何事情都运转正常,不过很快用户会发现存在一个异常,其中一个资源没有回滚。一个典型的错误致使的问题是利用一个 DataSourceTransactionManager以及一个利用Hibernate实现的仓库。

 

该用哪一个模式呢?

 

我会经过分析已介绍过的模式其利弊,帮助你们作出取舍。第一步是分析你的系统是否须要分布式事务。一个必须但不充分条件是,存在不止一个事务资源的单一处理。充分条件是这些资源都在一个单独的用例中,一般由系统架构的service层调用来驱动。

 

若是你不认为这是分布式事务,那最好采用Wing-and-a-Prayer模式,接着你会看见数据应该回滚但没有。或许你会看到这种影响从失败发生直至其下游一直存在,并且很难追溯回去。Wing-and-a-Prayer的 使用也可能会开发人员所疏忽,他们认为受到了XA保障,其实并没与配置底层资源加入到事务中。我曾经作过一个这样的项目,数据库是其余人员搭建的,他们在 安装数据库的过程当中关闭了XA支持。运行了个把月没有任何问题,接着各类奇怪的失败开始侵入业务处理中,须要花很长的时间去找出问题。

 

若是是一个包含异质资源的简单用例,你能够分析甚至作一些重构,那么非事务访问模式或 许是个不错的选择,特别是其中一个几乎是只读资源,双检测确保写操做。即使是失败了,非事务资源中的数据在业务术语中必须有意义。审核、版本控制、甚至日 志信息能很好的切入到此目录中,失败变得相对很日常—-任什么时候间真实事务中的任何事情均可回滚,但你需确信这样作不存在负面影响就好。

 

对系统而言,最大努力一阶段提交须要通用的失败保护机制,但有不存在2PC那么大的开销,并且性能获得极大的提高。相对非事务资源,它的创建须要更多的技巧,但无需太多的分析, 一般应用于更加通用的数据类型中。完成数据一致性的某些特性,须要保障业务处理对外层资源(”outer” resources:第一个提交的资源)而言是幕等式。消息驱动的数据库更新就是一个完美的例子,而且在Spring中获得很好的支持。不常见的场景须要一些额外的框架代码,这些代码终究会成为Spring的一部分。

 

共享资源模式是一种特定的例子,一般涉及一个特定的类型和平台两个资源,好比,ActiveMQ和任何一个RDBMS或者OracleAQ与一个Oracle数据库共存。这样作最大的收益是至关的灵活以及出色的性能。

 

Full XA with 2PC是一种通用模式,在应对多个异质资源事务 失败时提供很好的无忧保证。不利的是它的开销很大,须要遵循特定的I/O协议和特定的平台。有开源的JTA实现,提供了一种摆脱应用服务器的方式,但多数 开发人员依旧认为它们并不是最好。能够确信的是,人们花更多的时间去思考系统的事务界限,会更倾向于使用他们并不那么须要的JTA和XA。至少使用 Spring的开发人员,他们的业务逻辑无需知道事务如何被处理的,暂时无需考虑平台选择的问题。

 

来源: ImportNew - 乔永琪

连接:http://www.importnew.com/15812.html

相关文章
相关标签/搜索