消息队列入门(二)——如何利用消息队列实现分布式事务

242 阅读4分钟

这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

什么是事务?

什么是事务呢?在我的认知中,分布式事务就是为了保证数据的一致性和完整性,事务应具有四个属性:原子性,一致性,隔离性,持久性,简称 ACID特性。

原子性,是指一个事务操作不可分割,要么成功,要么失败,不能有一半成功一半失败的情况。

一致性,是指这些数据在事务执行完成这个时间点之前,读到的一定是更新前的数据,之后读到的一定是更新后的数据,不应该存在一个时刻,让用户读到更新过程中的数据。

隔离性,是指一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对正在进行的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性,是指一个事务一旦完成提交,后续的其他操作和故障都不会对事务的结果产生任何影响。

用电商来说,用户在购买商品时,先将商品加入到购物车中吗,再下订单完成支付,支付后,购物车中的商品将会被删除。将商品从购物车中删除这个步骤并不需要立即完成,所以我们可以将它放到消息队列中进行异步清理购物车。

创建订单,清理购物车,这两个步骤都有可能会发生异常失败,可能会导致类似于:创建了订单,但是并没有清理购物车,或者订单没创建完成,但是购物车里的商品却被清理掉了。

如何利用消息队列如何实现分布式事务?

1.首先,订单系统在消息队列上开启一个事务。

2.订单系统给消息服务器发送一个半消息

3.半消息发送成功后,订单系统就可以执行本地事务了,在订单库中创建一条订单记录,提交订单库的数据库事务。

4.如果订单创建成功,那就提交事务消息,购物车系统系统消费这条消息。

5.如果订单创建失败,那就回滚事务消息,购物车系统不会收到这条消息。

如果第4或5步提交事务消息失败的话,该如何解决呢?

Kafka直接抛出异常,我们可以在业务代码中反复重试提交,直到提交成功,或者删除之前创建的订单进行补偿。

接下来我们看看RocketMq如何实现分布式事务。

RocketMq如何实现分布式事务

RocketMq通过事务的反查机制来实现,如果订单系统在提交,或者回滚事务消息的时候,发生了网络异常导致MQ上的Broker没有收到提交或者回滚的请求,Broker会定期去Producer上也就是订单系统反查这个事务对应的本地事务的状态,根据反查的结果来决定是提交或者回滚这个事务。

所以,我们需要在业务代码中实现一个反查本地事务状态的接口,告知MQ本地事务是失败还是成功,我们只要根据消息中的订单ID,在数据库中查询订单是否存在,如果存在就返回成功,否则返回失败,MQ会根据结果提交或者回滚事务消息,即使发送事务的订单服务器挂了,MQ依然可以通过其他订单服务来查询,确保事务的一致性。

所以流程如下所示:

1.订单服务发送半消息给MQ。

2.MQ告知订单服务半消息发送成功。

3.订单服务执行本地事务。

4.订单服务根据本地事务执行的成功与否提交或者回滚事务消息。

5.如果一定时间MQ未收到来自订单服务的确认时,MQ会执行反查,调用订单服务的反查接口。

6.订单服务检查本地事务的状态。

7.根据本地事务的反查结果,MQ选择投递消息给购物车系统或者选择删除消息不投递。

今后我有时间我会画出具体的一个流程图,或者用伪代码来实现。