基于MQ,JTA实现多服务的分布式事务
Orderservice监听新订单队列中的消息,获取之后新增订单,成功则往新订单缴费队列中写消息,中间新增订单的过程使用JTA事务管理,当新增失败则事务回滚,不会往新订单缴费队列中写消息; 再比如User service 扣费成功后,往新订单转移票队列写消息,这时Ticket service 正在处理中或者处理中发生了失败,这中间的过程中用户查看自己的余额已经扣费成功,但票的信息却没有,此时可以使用事务失败回滚的方式依次回退,这种叫弱一致性;又或者可以把处理失败的内容发送至一个错误队列中,由人工处理等方式解决,这种叫最终一致性。
使用 Spring JTA
可以使用
- 如JBoss之类的应用服务器提供的JTA事务管理器
- Atomikos、Bitronix等库提供的JTA事务管理器
禁止使用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与最后资源博弈
代码语言:javascript复制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 transaction
1.2 共享资源
适用场景
两个数据源可共享同一底层资源时。
比如ActiveMQ使用DB作为底层资源存储,使用DB的connection控制事务提交,需要数据源支持指定底层资源存储方式。
1.3 最大努力一次提交
依次提交事务,可能会出错,尽量通过AOP或Listener实现事务直接的同步。
1.4 JMS最大努力一次提交 重试
适用场景
其中一个数据源是MQ,并且事务由读MQ消息开始。
利用MQ消息的重试机制,重试的时候需要考虑重复消息。
代码语言:javascript复制1.start message transaction
2.receive message
3.start database transaction
# 数据库操作出错,消息会被放回MQ,重试重新触发该方法
4.update database
5.commit database transaction
6.commit message transaction
这种时候没有问题,都能成功回滚。
代码语言:javascript复制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 链式事务管理
- 多数据源 链式事务、或其他事务同步方式
3 代码实战
DB-DB
application.properties中配置了两个数据源