直接参考:
面试 mp.weixin.qq.com/s/V6U2QhmG-… 这里面的XA2阶段,TCC要绝对记好了 本地消息表参考:juejin.cn/post/687289… 中的本地消息表
1、A 系统在自己本地一个事务里操作同时,插入一条数据到消息表;
2、接着 A 系统将这个消息发送到 MQ 中去;
3、B 系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其
他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不会重复
处理消息;
4、B 系统执行成功之后,就会更新自己本地消息表的状态以及 A 系统消息表的状态;
5、如果 B 系统处理失败了,那么就不会更新消息表状态,那么此时 A 系统会定时扫描自己
的消息表,如果有未处理的消息,会再次发送到 MQ 中去,让 B 再次处理;
6、这个方案保证了最终一致性,哪怕 B 事务失败了,但是 A 会不断重发消息,直到 B 那
边成功为止。
感悟:这里纯粹是靠本地消息实现“最终一致性”,这里没有要求MQ的可靠性,其实我觉得这里的方案有一点不好的是第4步,B系统要通知A系统更新消息表的状态,增加了很多的复杂性。
第4步如果两个服务中间是通过zk,可以通过zk的侦听器来实现B系统通知A系统更新消息表:参考https://mp.weixin.qq.com/s/V6U2QhmG-G3x2tH6vTEZ9w中的本地消息表设计.
基于消息中间件(rabbitMQ)实现最终一致性事务方案
1)order-service 中,
在 t_order 表添加订单记录 &&
在 t_local_msg 添加对应的扣减库存消息
【这两个过程要在一个事务中完成,保证过程的原子性。】
repo-service 中,
检查本次扣库存操作是否已经执行过 &&
执行扣减库存如果本次扣减操作没有执行过 &&
写判重表 &&
向 MQ sever 反馈消息消费完成 ACK
【这四个过程也要在一个事务中完成,保证过程的原子性。判重表就是消息表,通过MQ本身的消息ID(MSGID)做幂等判断.】
2)order-service 中有一个后台程序,源源不断地把消息表中的消息传送给消息中间件,成功后则删除消息表中对应的消息。
如果失败了,也会不断尝试重传。如果 order-service 发送给消息中间件的消息网络超时时,这时候消息中间件可能收到了消息但响应 ACK 失败,
也可能没收到,order-service 会再次发送该消息,直至消息中间件响应 ACK 成功,这样可能发生消息的重复发送,不过没关系,
只要保证消息不丢失,不乱序就行,后面 repo-service 会做去重处理。
3)消息中间件MQ向 repo-service 推送 repo_deduction_msg,repo-service 成功处理完成后会向中间件响应 ACK,
消息中间件收到这个 ACK 才认为 repo-service 成功处理了这条消息,否则会重复推送该消息。
但是有这样的情形:repo-service 成功处理了消息,向中间件发送的 ACK 在网络传输中由于网络故障丢失了,
导致中间件没有收到 ACK 重新推送了该消息。这也要靠 repo-service 的消息去重特性来避免消息重复消费。
4)在 2)和 3)中提到了两种导致 repo-service 重复收到消息的原因,一是生产者重复生产,二是中间件重传。为了实现业务的幂等性,repo-service 中维护了一张判重表(消息记录表),这张表中记录了被成功处理的消息的 id。repo-service 每次接收到新的消息都先判断消息是否被成功处理过,若是的话不再重复处理。
基于消息中间件的最终一致性全局事务方案是互联网公司在高并发场景中探索出的一种创新型应用模式,利用 MQ 实现微服务之间的异步调用、解耦合和流量削峰,支持全局事务的高并发,并保证分布式数据记录的最终一致性
----下面的参考,有时间可以看看-----
其他参考:
MySql XA 事务个人理解:
XA事务由一个或多个资源管理器(Resource Managers)、一个事务管理器(transaction manager)以及一个应用程序(application program)组成。
- 1,资源管理器(参与者):提供访问事务资源的方法。通常一个数据库就是一个资源管理器。 [微服务中各个服务所对应的数据库]
- 2,事务管理器(事务协调者):连接MySQL服务端的客户端,协调参与全局事务中的各个事务。 [我的理解是JavaWeb项目中依赖的mysql-client]
- 3,应用程序:定义事务的边界,指定全局事务中的操作。 [我的理解是JavaWeb项目]
XA事务下2PC的代码实现
参考 《MySQL技术内幕 InnoDB 存储引擎》中的例子