Spring的分布式事务,使用或不用XA - 6

17 篇文章 0 订阅
9 篇文章 0 订阅

原文链接:

http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html?page=6


Page 6 of 6

这个用于触发回滚的确切机制并不重要;有若干可选方案。重要的是提交或者回滚动作的发生和资源的业务操作顺序相反。在示范应用中,消息事务必须最后提交,因为业务处理的指令包含在那个资源中。之所以这很重要,是由于在这个失败(很少发生)中第一个提交成功第二个失败。由于从设计上而言,所有的业务处理在这个时间点已经结束,出现部分失败的唯一原因只能是消息中间件的基础设施出了问题。

注意如果数据库资源提交失败,那么效应只是一个回滚。所以仅有的非原子性失败模式是第一个事务提交后第二个进行回滚。更一般的,如果事务中有n个资源,那么有n-1个这样的失败模式,导致回滚某数据后部分资源处于一个不一致(已提交)的状态。在上面这个消息-数据库的案例中,

这种故障模式的结果是,该消息被回滚,然后回到另一个事务,即使它已被成功地处理了。因此你可以放心的假设可能发生的更糟糕的事情是,消息被重复传递。更一般的情况,由于事务前期的资源被认为可能潜在包含后续资源的处理信息,最终结果一般被称为消息重复(duplicate message)。

一些人觉得消息重复很少发生所以无需重视。但为了对业务数据的正确性和一致性更为自信,你需要在业务逻辑中意识到这一点。如果业务处理意识到消息会重复发送,要做的不过是(通常会有一些额外的成本,但远少于2PC)是检查数据是否已被处理,如果有则什么也不做。这在专业上有时被称为幂等业务服务(Idempotent Business Service)模式。

示例代码包括利用该模式同步事务性资源的两个例子。我将依次讨论它们,然后看看其他一些可选项。

Spring和消息驱动POJOs(Spring and message-driven POJOs)

示例代码best-jms-db项目中,参与者都使用主流的配置选项,遵循最大努力单阶段提交(Best Efforts 1PC)模式。想法是发送到一个队列中的消息被异步侦听器所选择,并且用于将数据插入到数据库中的表。

TransactionAwareConnectionFactoryProxy -- Spring中被用于这个模式的一个部件 -- 在这里是关键因素。这个配置把ConnectionFactory包装在一个装饰器中来处理事务同步,而不是使用原有的ConnectionFactory。具体细节在文件jms-context.xml中,如列表6所示:

列表6. 配置一个TransactionAwareConnectionFactoryProxy来包装ActiveMQ提供的JMS ConnectionFactory
<bean id="connectionFactory"
  class="org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy">
  <property name="targetConnectionFactory">
    <bean class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="brokerService">
      <property name="brokerURL" value="vm://localhost"/>
    </bean>
  </property>
  <property name="synchedLocalTransactionAllowed" value="true" />
</bean>

ConnectionFactory不需要 知道和哪个 事务 管理器同步 , 因为当需要时只有一个 事务 处于激活状态 , 这是在Spring 内部处理的 。 驱动 (driving)事务 是由一个 通用 的DataSourceTransactionManager 配置在 data-source-context.xml 中。 这个组件需要 了解 事务管理器是 JMS 侦听容器, 将 轮询 和接收消息:

<jms:listener-container transaction-manager="transactionManager" >
  <jms:listener destination="async" ref="fooHandler" method="handle"/>
</jms:listener-container>

fooHandler和method告诉侦听容器在一个“异步”消息到来时该调用哪个组件的哪个方法。该处理器程序实现如下,接受一个字符串作为输入信息,并用它来插入一个记录:

public void handle(String msg) {

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

为模拟失败,这个代码使用了一个 FailureSimulator切面(aspect)。

它检查消息内容是否会失败,以什么样的方式失败。列表7中的maybeFail()方法,在FooHandler处理完消息,但在事务结束前被调用,因此它可以影响事务的结果:

列表7. 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会话。预期的结果,这是一部分提交:数据库的工作保持承诺但消息回滚。这是在asynchronousmessagetriggerandpartialrollbacktests单元测试。

示例包还包括一个完全成功提交的事务的单元测试,在AsynchronousMessageTriggerSunnyDayTests类中。

同一个JMS配置和相同的业务逻辑,也可用于同步设置,这里消息是在一个锁住的业务逻辑内部调用中被接收,而不是代理给一个侦听容器。这种方法在best-jms-db示范项目中演示过。顺利提交和完整回滚的案例分别在SynchronousMessageTriggerSunnyDayTests和SynchronousMessageTriggerAndRollbackTests中被测试。


by iefreer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值