如何解决下单系统这个高并发里边的分布式事务呢?

首先,我们肯定不会用 2PC 模式、 TCC-事务补偿性方案,我们也不考虑

最终我们选择可靠消息+最终一致性这种方式

为了保证高并发,订单这一块还是自己回滚,

库存服务自己怎么回滚?

有两种解决办法

第一种

我们在提交订单那里,当捕捉到异常要回滚的时候,给库存服务发一个消息,让库存服务自己把库存解锁

这样不需要让库存事务回滚,只需要给它发一个消息,不会损失什么性能

第二种

库存服务本身也可以使用自动解锁模式。

怎么自动解锁呢?

需要使用消息队列来完成。

image.png

如果你想让我这的哪些库存解锁,首先你需要给我发一个消息告诉我。

然后我们专门的库存解锁服务,去来订阅我们stock.release.stock.queue这个队列里的消息。

那你给我发消息的时候,比如:用路由键stock.release,我知道要库存解锁,

然后,你的消息发给我们这个交换机stock-event-exchange

交换机把这个消息路由给stock.release.stock.queue这个队列。

然后,这个队列stock.release.stock.queue里边存的这些消息都是库存要解锁的消息,我们的库存解锁服务只要收到了,它就会在后台慢慢的解锁消息。

我们不用保证强一致,我们哪怕是二十分钟、三十分钟,乃至于一天以后把这个库存解锁了,最终一致了就行。

所以我们可以来使用消息队列来完成我们的这个最终一致性。

锁库存的增强版逻辑

image.png

我们想要锁库存的话,我们先来保存一个库存工作单和库存工作单详情

相当于只要我们想要锁库存,我们先给数据库里边保存记录,我要锁库存。

接下来我们就来进行锁,只要锁成功了,那一切ok。

如果锁失败了,数据库里边相当于没有这个锁库存记录。

因为锁失败呢,我们这个本身自己所失败会全部回滚。

但如果可能是这种失败,比如我们来到订单里边,我们库存其实自己锁成功了。但是我们订单下边的其他完了,然后库存要进行解锁。那怎么办呢?

我们可以使用定时任务

订单服务的完整消息队列

image.png

库存自动解锁

image.png

库存微服务,有一个它的库存交换机stock-event-exchange.

如果想要解锁库存,应该是这样的。

首先订单创建成功之后,库存锁定成功,然后发一个消息给交换机,

这个消息里面的内容有订单编号、仓库编号、哪个商品锁了几个库存,

这个交换机,绑定了两个队列,

一个是按照stock.release.#模糊匹配的路由键绑定的stock.release.stock.queue队列

一个是stock.delay.queue队列

第一次发的库存锁定成功的消息,先使用路由键叫stock.locked

交换机按照这个路由键,找到stock.delay.queue延时队列

延时队列50分钟以后,用stock.release这个路由键,将死信交给库存交换机stock-event-exchange

交换机收到以后,按照这个路由键查找,发现stock.release.#这个模糊匹配的路由键跟它是一样的,然后被交换机路由到我们这个stock.release.stock.queue队列。

接下来的解锁库存服务,专门来处理stock.release.stock.queue里的消息。

最终实现

柔性事务- 可靠消息+最终一致性方案(异步确保型)
使用MQ时保证消息可靠(丢失,重复,加压)和最终一致性。

7.24

image.png

7.23

image.png
具体代码

定时关闭订单

image.png

首先订单创建成功之后,使用order.create.order路由键将消息路由到order-event-exchange交换机

交换机发现order.create.order这个路由键绑定的是order.delay.queue这个延时队列,然后就把它放到order.delay.queue队列里

过了30分钟,这个延时队列里面的消息,也就是死信,通过order.release.order又路由回order-event-exchange交换机

然后交换机发现这个路由键对应的是order.release.order.queue这个队列,然后就放到order.release.order.queue这个队列里

最终监听order.release.order.queue这个队列的释放订单服务,发现有消息进来了,就会针对里面的数据对其进行关闭订单

问题

这种关闭订单方式会有一些问题

假设订单创建成功之后,订单服务的机器由于卡顿、消息延迟等原因,导致订单未及时取消

此时库存服务的逻辑是订单创建成功之后,它自己会发一个消息,等 40分钟 以后检查之前下单的订单是否已取消,如果是已取消,则解锁库存

结果,库存服务过来查询时,订单服务由于上述原因没有将订单修改为已取消,所以库存就不会解锁,此时的库存消息就算是消费了

等库存服务都检查完了,此时的订单服务才反应过来,然后把订单状态改为已取消了,但是此时库存服务会再有任何的操作了,因为检查订单的消息已经被消费了,库存永远得不到解锁。

解决

为了解决这个问题,我们在监听取消订单的消息时,再发一个消息,主动解锁库存。

主动解锁库存

image.png

具体是这样的,在释放订单之后,我们主动发一个消息解锁库存,

使用order.release.other将消息路由到交换机,

交换机根据order.release.other.#匹配到stock.release.stock.queue这个队列,并将消息发了过去,

库存服务有对这个队列进行监听,所有一旦有数据来了,就会对其进行解锁库存服务