大数据开发:消息队列如何实现分布式事务?

2021-07-12 18:04:54 浏览数 (2)

在大数据技术生态当中,消息队列,主要是针对实时消息流的处理,而实时消息流场景下,常常需要解决的一个问题,就是数据一致性的问题,这其中又涉及到分布式事务。今天的大数据开发学习分享,我们就来讲讲消息队列如何利用事务消息实现分布式事务?

消息队列中的事务主要解决的是消息生产者和消息消费者的数据一致性问题。

拿电商来举个例子,一般来说,用户在电商APP上购物时,先把商品加到购物车里,然后几件商品一起下单,最后支付,完成购物流程,就可以等待收货了。这个过程中有一个需要用到消息队列的步骤,订单系统创建订单后,发消息给购物车系统,将已下单的商品从购物车中删除。因为从购物车删除已下单商品这个步骤,并不是用户下单支付这个主要流程中必需的步骤,使用消息队里来异步清理购物车是更加合理的设计图片对于订单系统来说,它创建订单的过程中实际上执行了2个步骤的操作:

在订单库中插入一条订单数据,创建订单。

发消息给消息队列,消息的内容就是刚刚创建的订单。

购物车系统订阅相应的主题,接收订单创建的消息,然后清理购物车,在购物车中删除订单中的商品。

问题的关键点集中在订单系统,创建订单和发送消息这两个步骤要么都操作成功,要么都操作失败,不允许一个成功而另一个失败的情况出现。

1、什么是分布式事务?

事务的4个特性(ACID):

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

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

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

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

事务消息适用的场景主要是那些需要异步更新数据,并且对数据实时性要求不太高的场景。

比如订单系统的例子,在创建订单后,如果出现短暂的几秒,购物车里的商品没有及时情况,也不是完全不可接受的,只要最终购物车的数据和订单数据保持一致就可以了。

2、消息队列是如何实现分布式事务的?

回到订单和购物车这个例子,首先,订单系统在消息队列上开启了一个事务。然后订单系统给消息服务器发送一个半消息,这个半消息包含的内容是完整的消息内容,和普通消息的唯一区别是,在事务提交之前,对于消费者来说,这个消息是不可见的。

半消息发送成功后,订单系统就可以执行本地事务了,在订单库中创建一条订单记录,并提交订单库的数据库事务。然后根据本地事务的执行结果决定提交或者回滚事务消息。如果订单创建成功,那就提交事务消息,购物车系统就可以消费到这条消息继续后续的流程。如果订单创建失败,那就回滚事务消息,购物车系统就不会收到这条消息。这样就基本实现了要么都成功,要么都失败的一致性要求。

如果在第四步提交事务消息时失败了,Kafka会直接抛出异常,让用户自行处理,可以在业务代码中反复重试提交,直到提交成功,或者删除之前创建的订单进行补偿。

3、RocketMQ中的分布式事务实现

在RocketMQ中的事务实现中,增加了事务反查的机制来解决事务消息提交失败的问题。如果Producer也就是订单系统,在提交或者回滚事务消息时发生网络异常,RocketMQ的Broker没有收到提交或者回滚的请求,Broker会定期去Producer上反查这个事务对应的本地事务的状态,然后根据反查结果决定提交或者回滚这个事务。

为了支撑这个事务反查机制,业务代码中需要实现一个反查本地事务状态的接口,告知RocketMQ本地事务是成功还是失败。

在订单系统的例子中,反查本地事务的逻辑只要根据消息中的订单ID,在订单库中查询这个订单是否存在即可,如果订单存在则返回成功,否则返回失败。RocketMQ会自动根据事务反查的结果提交或者回滚事务消息。

这个反查本地事务的实现,并不依赖消息的发送方,也就是订单服务的某个实例节点上的任何数据。这种情况下,即使是发送事务消息的那个订单服务节点宕机了,RocketMQ依然可以通过其他订单服务的节点来执行反查,确保事务的完整性。

关于大数据开发,消息队列如何实现分布式事务,以上就为大家做了基本的介绍了。消息队列在分布式事务方面,需要大家多去理解和深入,主流的解决方案,也要知晓。

0 人点赞