博客 / 詳情

返回

RocketMQ 事務消息初體驗

事務消息是 RocketMQ 的高級特性之一 。這篇文章,筆者會從應用場景功能原理實戰例子三個模塊慢慢為你揭開事務消息的神秘面紗。

1 應用場景

舉一個電商場景的例子:用户購物車結算時,系統會創建支付訂單。

用户支付成功後支付訂單的狀態會由未支付修改為支付成功,然後系統給用户增加積分。

通常我們會使用普通消費方案,該方案能夠發揮 MQ 的優勢:異步解耦 , 同時架構設計非常簡單。

  1. 用户購物車結算時,系統創建支付訂單;
  2. 支付成功後,更新訂單的狀態從未支付修改為支付成功;
  3. 發送一條普通消息到消息隊列服務端;
  4. 積分服務消費消息,添加積分記錄。

但該方案有個非常直觀的缺點:容易出現不一致的現象

  1. 假如先發送消息,後修改訂單狀態,消息發送成功,訂單沒有執行成功,需要回滾整個事務(訂單數據事務回滾,積分服務消費時,需要先反查事務狀態,若事務提交,才插入積分記錄)。
  2. 假如先修改訂單狀態,後發送消息,訂單狀態修改成功,但消息發送失敗,需要補償操作才能保持最終一致。
  3. 假如先修改訂單,後發送消息,訂單狀態修改成功,但消息發送超時,此時無法判斷需要回滾訂單還是提交訂單變更。

我們看到,為了完善普通消費方案,業務層還需要做到兩點:補償機制提供事務狀態查詢接口

要做到這兩點,難不難呢?

不難,但是業務層代碼會比較混亂,更優的方案還是得從中間件層面解決。

2 功能原理

RocketMQ 事務消息是支持在分佈式場景下保障消息生產和本地事務的最終一致性。交互流程如下圖所示:

1、生產者將消息發送至 Broker 。

2、Broker 將消息持久化成功之後,向生產者返回 Ack 確認消息已經發送成功,此時消息被標記為"暫不能投遞",這種狀態下的消息即為半事務消息

3、生產者開始執行本地事務邏輯

4、生產者根據本地事務執行結果向服務端提交二次確認結果( Commit 或是 Rollback ),Broker 收到確認結果後處理邏輯如下:

  • 二次確認結果為 Commit :Broker 將半事務消息標記為可投遞,並投遞給消費者。
  • 二次確認結果為 Rollback :Broker 將回滾事務,不會將半事務消息投遞給消費者。

5、在斷網或者是生產者應用重啓的特殊情況下,若 Broker 未收到發送者提交的二次確認結果,或 Broker 收到的二次確認結果為 Unknown 未知狀態,經過固定時間後,服務端將對消息生產者即生產者集羣中任一生產者實例發起消息回查

  1. 生產者收到消息回查後,需要檢查對應消息的本地事務執行的最終結果。
  2. 生產者根據檢查到的本地事務的最終狀態再次提交二次確認,服務端仍按照步驟4對半事務消息進行處理。

筆者認為事務消息的精髓在於:

  1. 本地事務執行成功,消費者才能消費事務消息
  2. 消息回查本身就是補償機制的實現,事務生產者需提供了事務狀態查詢接口。

3 實戰例子

為了便於大家理解事務消息 ,筆者新建一個工程用於模擬支付訂單創建支付成功贈送積分的流程。

首先,我們創建一個真實的訂單主題:order-topic

然後在數據庫中創建三張表 訂單表事務日誌表積分表

最後我們創建一個 Demo 工程,生產者模塊用於創建支付訂單、修改支付訂單成功,消費者模塊用於新增積分記錄。

接下來,我們展示事務消息的實現流程。

1、創建支付訂單

調用訂單生產者服務創建訂單接口 ,在 t_order 表中插入一條支付訂單記錄。

2、調用生產者服務修改訂單狀態接口

接口的邏輯就是執行事務生產者的 sendMessageInTransaction 方法。

生產者端需要配置事務生產者事務監聽器

發送事務消息的方法內部包含三個步驟 :

事務生產者首先發送半事務消息,發送成功後,生產者才開始執行本地事務邏輯

事務監聽器實現了兩個功能:執行本地事務供 Broker 回查事務狀態

執行本地事務的邏輯內部就是執行 orderService.updateOrder 方法。

方法執行成功則返回 LocalTransactionState.COMMIT_MESSAGE , 若執行失敗則返回 LocalTransactionState.ROLLBACK_MESSAGE

需要注意的是: orderService.updateOrder 方法添加了事務註解,並將修改訂單狀態和插入事務日誌表放進一個事務內,避免訂單狀態和事務日誌表的數據不一致。

最後,生產者根據本地事務執行結果向 Broker 提交二次確認結果

Broker 收到生產者確認結果後處理邏輯如下:

  • 二次確認結果為 Commit :Broker 將半事務消息標記為可投遞,並投遞給消費者。
  • 二次確認結果為 Rollback :Broker 將回滾事務,不會將半事務消息投遞給消費者。

3、積分消費者消費消息,添加積分記錄

當 Broker 將半事務消息標記為可投遞時,積分消費者就可以開始消費主題 order-topic 的消息了。

積分消費者服務,我們定義了消費者組名,以及訂閲主題消費監聽器

在消費監聽器邏輯裏,冪等非常重要 。當收到訂單信息後,首先判斷該訂單是否有積分記錄,若沒有記錄,才插入積分記錄。

而且我們在創建積分表時,訂單編號也是唯一鍵,數據庫中也必然不會存在相同訂單的多條積分記錄。

4 總結

RocketMQ 事務消息是支持在分佈式場景下保障消息生產和本地事務的最終一致性

編寫一個實戰例子並不複雜,但使用事務消息時需要注意如下三點:

1、事務生產者和消費者共同協作才能保證業務數據的最終一致性;

2、事務生產者需要實現事務監聽器,並且保存事務的執行結果(比如事務日誌表) ;

3、消費者要保證冪等。消費失敗時,通過重試告警+人工介入等手段保證消費結果正確。

本文涉及到的工程源碼,筆者已上傳到 Github ,感興趣的同學可以瞭解一下,若有疑問直接加筆者好友,一起交流技術,一起成長。

筆者會在後續的文章裏,詳細解析事務消息的實現原理,敬請期待。


實戰代碼地址:

https://github.com/makemyownlife/rocketmq4-learning

如果我的文章對你有所幫助,還請幫忙點贊、在看、轉發一下,你的支持會激勵我輸出更高質量的文章,非常感謝!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.