基于MQ,JTA实现多服务的分布式事务
Orderservice监听新订单队列中的消息,获取之后新增订单,成功则往新订单缴费队列中写消息,中间新增订单的过程使用JTA事务管理,当新增失败则事务回滚,不会往新订单缴费队列中写消息;
再比如User service 扣费成功后,往新订单转移票队列写消息,这时Ticket service 正在处理中或者处理中发生了失败,这中间的过程中用户查看自己的余额已经扣费成功,但票的信息却没有,此时可以使用事务失败回滚的方式依次回退,这种叫弱一致性;又或者可以把处理失败的内容发送至一个错误队列中,由人工处理等方式解决,这种叫最终一致性。
可以使用
- 如JBoss之类的应用服务器提供的JTA事务管理器
- Atomikos、Bitronix等库提供的JTA事务管理器
- 为什么禁用JTA ? 因为JTA采用两阶段提交方式:
- 第一次是预备阶段
- 第二次才是正式提交
当第一次提交出现错误,则整个事务出现回滚,一个事务的时间可能会较长,因为它要跨越多个数据库多个数据资源的的操作,所以在性能上可能会造成吞吐量低。
那既然不使用 JTA,如何实现事务呢? 依次提交两事务:
- start MQ transaction
- receive message 读取消息 出错时,message transaction直接回滚
- start DB transaction
- update DB更新数据库 出错时,由于此时database transaction、message transaction都尚未提交,这时虽然已经读取了消息,但只要 MQ 支持事务功能,消息就会被回滚,重新放回MQ,重试重新触发该方法
- commit DB transaction 出错时,和上一点原因相同
- commit MQ transaction 出错时,database transaction已被提交了,所以无法回滚。MQ 事务尚未提交,所以可直接回滚。这也就是不使用 JTA 时遇到的最大难题。所以 spring 也提供了很多机制保障
消息放回至MQ队列,重试重新触发该方法 当这一步出现错误时,上面的因为已经commit,所以不会rollback
1 多数据源的事务同步解决方案 1.1 XA与最后资源博弈1.start MQ transaction 2.receive message # 针对 DB 使用 JTA 事务 3.start JTA transaction on DB 4.update DB # DB 一阶段提交 5.phase-1 commit on DB transaction # 当该步出错时,由于DB 还在XA的第一次提交预备状态,DB 还是可以回滚 6.commit MQ transaction # 等到 MQ 事务提交完成,才做 DB 二阶段提交 # 该步出错时,因为MQ不是XA方式,提交后无法回滚,虽然 DB 都可以回滚 7.phase-2 commit on DB transaction1.2 共享资源 适用场景
两个数据源可共享同一底层资源时。
比如ActiveMQ使用DB作为底层资源存储,使用DB的connection控制事务提交,需要数据源支持指定底层资源存储方式。
1.3 最大努力一次提交依次提交事务,可能会出错,尽量通过AOP或Listener实现事务直接的同步。
1.4 JMS最大努力一次提交+重试 适用场景其中一个数据源是MQ,并且事务由读MQ消息开始。
利用MQ消息的重试机制,重试的时候需要考虑重复消息。
1.start message transaction 2.receive message 3.start database transaction # 数据库操作出错,消息会被放回MQ,重试重新触发该方法 4.update database 5.commit database transaction 6.commit message transaction
这种时候没有问题,都能成功回滚。
1.start message transaction 2.receive message 3.start database transaction 4.update database 5.commit database transaction # 提交MQ事务出错,消息放回MQ,重试重新触发该方法 6.commit message transaction
DB 已经提交了,所以这时会重复 DB操作,因为DB transaction不是使用JTA事务管理,所以DB已经commit成功. 那如何避免呢,需要忽略重发消息,比如唯一性校验等手段。
1.5 链式事务管理定义一个事务链,多个事务在一个事务管理器里依次提交。 依旧可能出错。
2 事务方案选型 业务一致性要求- 强一致性事务 JTA(性能最差、只适用于单个服务内)
- 弱、最终一致性事务 最大努力一次提交、链式事务(设计相应的错误处理机制)
- MQ-DB 最大努力一次提交+重试
- 多DB 链式事务管理
- 多数据源 链式事务、或其他事务同步方式
application.properties中配置了两个数据源