简单聊聊消息队列的事务补偿机制

https://my.oschina.net/u/1589819/blog/1503241

因为一直学习与尝试负责公司的推送相关业务,包括整个应用的实现,其中就采用了基于消息队列的异步事件驱动模型来做解耦异步处理,所以就要去做了解一些相关的知识点,这边稍作总结,并整理一下消息补偿机制的一套简单实现的代码设计图。

采用基于消息队列的异步事件驱动模型来解决问题的时候,一个计较棘手的问题就是事务的一致性。

案例:现在用户发起一个创建订单的请求,如果我们是单系统架构,那么修改订单表,修改库存表可能都是在同一个事务中完成,所以轻而易举就达到了事物一致性原则,但是这不是我们要讨论的,所以就带过。现在微服务架构在互联网公司大火特火,热度未减,分布式是事务也成为了一个亟待解决的问题,阿里云GTS标榜如何让分布式事务更简单。

比如,用户发起一个创建订单的请求,首先在订单服务上生成了新的订单,同时还要去库存服务中减去库存,因为是分布式架构,所以库存扣减与订单创建可能是在两个遥远的机器上,如果想要通过本地事务来解决那几乎是不可能的,保证两个事务之间的状态一致性——订单创建成功,库存扣减失败,如何回滚订单?一直都是分布式架构中绕不开的挑战。

分布式架构中如何解决事务问题,在很多技术群都上都在讨论,比如dubbo , spring cloud等等。目前还没有接触到这方面相关知识,后续如果有幸参与,可做分享,本次想要聊的是假基于消息队列的异步事件驱动是如何解决如上的分布式问题,以及如何保证事务一致性。

事务一致性原则(ACID):

  • Atomicity - 原子性,改变数据状态要么是一起完成,要么一起失败

  • Consistency - 一致性,数据的状态是完整一致的

  • Isolation - 隔离线,即使有并发事务,互相之间也不影响

  • Durability - 持久性, 一旦事务提交,不可撤销

订单创建完成之后,发送一个createOrderEvent到消息队列中,由消息队列负责转发给订阅该消息的消费者进行处理。

好,这个时候如果消息消费 成功,但是库存不足,库存扣减失败,订单创建则不能成功,这个时候很好处理,由库存服务推送一个subInventoryFail给到订单服务,订单服务根据消息将订单转为失败状态。

1、从用户体验的角度来说,整个过程是异步的,所以对于用户的体验来说,就做不到“立马成功或立马失败”的效果。

2、从技术的角度来说,整个过程你不再关注同一个事物的问题,而是关注最终订单的状态是否一致。【注:从分布式事务<-->最终一致性】保证事务最终一致性,但是基于这种事件驱动达到最终一致,解耦事务的成功实施需要依赖几个因素。

a、消息的投递是否可靠。

b、消息的可靠性,例如订单服务已经成功创建订单,但是还没来得及发送消息就宕机或者各种原因,导致订单的状态不一致。

基于以上两点的考虑,我们使用了一种基于本地事务的方案来保证消息最终的一致性。

创建订单与创建消息事件都在本地事务中,属于同一个事务,可以保证订单表与消息事件表的数据一致性。发送消息到消息中间件,在事务提交之后发送。到了库存服务的时候,启动一个定时任务去扫描消息事件表,将未投递失败/消费 失败的消息进行消费,即补偿事务一致性。

定时任务的方案可能不是最佳的,可以稍作改定,比如采用阿里巴巴开源的Canal

公司目前也是采用这种架构来解决订单与库存问题。有网友的做法是保证消息投递的可靠性,我们则是保证消费的一致性,具体的文章点我>>

可以将消息队列的进行封装,做成了一个starter,代码设计上大致如图下:


没有更多推荐了,返回首页