寫數據庫mq消息事務一致性解決方案


原文鏈接:https://cloud.tencent.com/developer/article/1478827

如果我們要在服務化拆分中使用消息隊列,那么我們需要解決哪些問題呢?首先去哪兒網提供了旅游產品在線預訂服務,那么就涉及電商交易,在電商交易中我們認為數據的一致性是非常關鍵的要素。那么我們的 MQ 必須提供一致性保證。

MQ 提供一致性保證又分為兩個方面。發消息時我們如何確保業務操作和發消息是一致的,也就是不能出現業務操作成功消息未發出或者消息發出了但是業務並沒有成功的情況。舉例來說,支付服務使用消息通知出票服務,那么不能出現支付成功,但是消息沒有發出,這會引起用戶投訴;但是也不能出現支付未成功,但是消息發出最后出票了,這會導致公司損失。總結一下就是發消息和業務需要有事務保證。一致性的另一端是消費者,比如消費者臨時出錯或網絡故障,我們如何確保消息最終被處理了。那么我們通過消費 ACK 和重試來達到最終一致性。

三、利用數據庫事務解決一致性問題

提到一致性,大家肯定就想到事務,而一提到事務,肯定就想到關系型數據庫,那么我們是不是可以借助關系型 DB 里久經考驗的事務來實現這個一致性呢。我們以 MySQL 為例,對於 MySQL 中同一個實例里面的 db,如果共享相同的 Connection 的話是可以在同一個事務里的。以下圖為例,我們有一個 MySQL 實例監聽在 3306 端口上,然后該實例上有 A,B 兩個 DB,那么下面的偽代碼是可以跑在同一個事務里的

有了這層保證,我們就可以透明的實現業務操作和消息發送在同一個事務里了,首先我們在公司所有 MySQL 實例里初始化出一個 message db,這個可以放到自動化流程中(據說在去哪兒由運維團隊完成),對應用透明。然后我們只要將發消息與業務操作放到同一個 DB 事務里即可。

我們來看一個實際的場景,在支付場景中,支付成功后我們需要插入一條支付流水,並且發送一條支付完成的消息通知其他系統。那么這里插入支付流水和發送消息就需要是一致的,任何一步沒有成功最后都會導致問題。那么就有下面的代碼

上面的代碼可以用下面的偽代碼解釋

實際上在 producer.sendMessage 執行的時候,消息並沒有通過網絡發送出去,而僅僅是往業務 DB 同一個實例上的消息庫插入一條記錄,然后注冊事務的回調,在這個事務真正提交后消息才從網絡發送出去,這個時候如果發送到 server 成功的話消息會被立即刪除掉。而如果消息發送失敗則消息就留在消息庫里,這個時候我們會有一個補償任務會將這些消息從消息庫里撈出然后重新發送,直到發送成功。整個流程就如下圖所示

1、begin tx 開啟本地事務

2、do work 執行業務操作

3、insert message 向同實例消息庫插入消息

4、end tx 事務提交

5、send message 網絡向 server 發送消息

6、reponse server 回應消息

7、delete message 如果 server 回復成功則刪除消息

8、scan messages 補償任務掃描未發送消息

9、send message 補償任務補償消息

10、delete messages 補償任務刪除補償成功的消息


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM