京東二面:如何利用RocketMQ事務消息,來實現(xiàn)分布式事務?比如下單清空購物車的場景
前言
大家好,我是田螺。
分享一道京東的場景面試題:如何利用RocketMQ事務消息實現(xiàn)分布式事務?比如下單清空購物車場景。
我們如何更好回答這個問題呢?可以按照以下這幾個維度:
- 什么是分布式事務?
- 一條普通消息,從產生到消費歷程
- 消息隊列的事務消息流程
- 事務消息的下單清空購物車場景
1. 什么是分布式事務
分布式事務是指在多個獨立的系統(tǒng)或數(shù)據(jù)庫之間執(zhí)行的事務操作,它確保所有參與者要么全部成功提交,要么全部回滾,以維持數(shù)據(jù)一致性。
通常保證分布式事務的方案有:消息隊列的事務消息、TCC(Try-Confirm-Cancel)模式、Seata、二階段提交等。
2.一條消息從產生到消費歷程
我們先來看看:RocketMQ的一條普通的消息,從產生到被消費,經歷的流程:
圖片
- 生產者產生消息,發(fā)送到MQ服務器(Broker)
- MQ服務器收到消息后,將消息持久化到存儲系統(tǒng)。
- MQ服務器返回ACk到生產者。
- 消費者從MQ服務器(Broker)拉取消息(Pull或Push,可以配置的)
- 消費者消費完消息,響應ACK
- MQ服務器收到ACK,認為消息消費成功,即在存儲中刪除消息。
其實,在RocketMQ中,消費完消息后,消息不會立即被刪除,而是根據(jù)消息的過期時間和消費進度進行管理。消費者確認消費后,RocketMQ會記錄該消息的消費狀態(tài),并在達到設定的過期時間后,從存儲中刪除該消息。這種機制確保了消息的可靠性和持久性。
3. 消息隊列的事務消息流程
在開始之前,我們先來了解一下,什么是半消息?
這個半消息,它不是說消息內容不完整,它包含的內容就是完整的消息內容。它跟普通消息的唯一區(qū)別是,在事務提交之前,對于消費者來說,這個消息是不可見的。
圖片
- 生產者產生消息,發(fā)送一條半消息到MQ服務器。
- MQ收到消息后,將消息持久化到存儲系統(tǒng),這條消息的狀態(tài)是待發(fā)送狀態(tài)。
- MQ服務器返回ACK確認到生產者。
- 生產者執(zhí)行本地事務
- 如果本地事務執(zhí)行成功,即commit執(zhí)行結果到MQ服務器;如果執(zhí)行失敗,發(fā)送rollback。
- 如果是正常的commit,MQ服務器更新消息狀態(tài)為可發(fā)送;如果是rollback,即刪除消息。
- 如果消息狀態(tài)更新為可發(fā)送,則MQ服務器會push消息(可配置push還是pull)給消費者。消費者消費完就回ACK。
- 如果MQ服務器長時間沒有收到生產者的commit或者rollback,它會反查生產者,然后根據(jù)查詢到的結果執(zhí)行最終狀態(tài)。
4. 事務消息下的單清空購物車場景
我們看下,下訂單清空購物車的例子吧。訂單系統(tǒng)創(chuàng)建完訂單后,然后發(fā)消息給下游系統(tǒng)的購物車系統(tǒng),清空購物車。
- 生產者(訂單系統(tǒng))產生消息,發(fā)送一條半消息到MQ服務器
- MQ收到消息后,將消息持久化到存儲系統(tǒng),這條消息的狀態(tài)是待發(fā)送狀態(tài)。
- MQ服務器返回ACK確認到生產者,此時MQ不會觸發(fā)消息推送事件
- 生產者執(zhí)行本地事務(訂單創(chuàng)建成功,提交事務消息)
- 如果本地事務執(zhí)行成功,即commit執(zhí)行結果到MQ服務器;如果執(zhí)行失敗,發(fā)送rollback。
- 如果是commit正常提交,MQ服務器更新消息狀態(tài)為可發(fā)送;如果是rollback,即刪除消息。
- 如果消息狀態(tài)更新為可發(fā)送,則MQ服務器會push消息給消費者(購物車系統(tǒng))。消費者消費完(即拿到訂單消息,清空購物車成功)就應答ACK。
- 如果MQ服務器長時間沒有收到生產者的commit或者rollback,它會反查生產者,然后根據(jù)查詢到的結果(回滾操作或者重新發(fā)送消息)執(zhí)行最終狀態(tài)。
有些伙伴可能有疑惑,如果消費者消費失敗怎么辦呢?那數(shù)據(jù)是不是不一致啦?所以就需要消費者消費成功,執(zhí)行業(yè)務邏輯成功,再反饋ack嘛。如果消費者消費失敗,那就自動重試嘛,接口支持冪等即可。