怎樣基於消息隊列來實現分佈式事務

大家好,我是狼王,一個愛打球的程序員

事務想必大家並不陌生,比如經常被人提起的ACID,但是爲了後續的分佈式事務的內容,我們先來聊聊 ACID,然後再介紹下什麼是分佈式事務,最後着重講下基於可靠消息的分佈式事務解決方案。

什麼是事務

嚴格意義上的事務應該是具備原子性、一致性、隔離性和持久性,簡稱 ACID

  1. 原子性(Atomicity),可以理解爲一個事務內的所有操作要麼都執行,要麼都不執行。

  2. 一致性(Consistency),可以理解爲數據是滿足完整性約束的,也就是不會存在中間狀態的數據,比如你錢包有 100,我錢包有 100,你給我打 50 塊,此時你錢包的錢應該是 50,我錢包的錢應該是 150,不會存在我錢加了,你錢沒扣的中間狀態。

  3. 隔離性(Isolation),指的是多個事務併發執行的時候不會互相干擾,即一個事務內部的數據對於其他事務來說是隔離的。

  4. 持久性(Durability),指的是一個事務完成了之後數據就被永遠保存下來,之後的其他操作或故障都不會對事務的結果產生影響。

而通俗意義上事務就是爲了使得一些更新操作要麼都成功,要麼都失敗

什麼是分佈式事務

分佈式事務顧名思義就是要在分佈式系統中實現事務,它其實是由多個本地事務組合而成。

一次大的操作由不同的小操作組成的,這些小的操作分佈在不同的服務器上,分佈式事務需要保證這些小操作要麼全部成功,要麼全部失敗。從本質上來說,分佈式事務就是爲了保證不同數據庫數據一致性

常見的分佈式事務的解決方案有以下幾種:2PC,3PC,TCC,本地消息表、可靠消息最終一致性、盡最大努力通知

今天我們就着重講講可靠消息最終一致性的解決方案

什麼是可靠消息最終一致性方案

可靠消息最終一致性方案是指當事務發起方執行完成本地事務後發出消息到消息中間件事務參與方(消息消費者)一定能夠接收到消息並處理事務成功,此方案強調的是只要消息發給事務參與方,則最終事務要達到一致

這個方式存在哪些問題?

此方案是通過消息中間件實現的,事務發起方(消息生產方)將消息發給消息中間件,事務參與方從消息中間件接收消息,由於網絡通信的不確定性會導致分佈式事務問題,如下圖:

  1. 本地事務與消息的原子性問題

如上圖在虛線框內,存在以下幾種情況:

1)本地事務提交失敗,則消息不發送。
2)本地事務成功,消息發送失敗,本地事務回滾。
3)本地消息成功,消息超時,本地事務回滾,消息最終失敗。
4)本地消息成功,消息超時,本地事務回滾,消息最終成功。

綜上所述,存在第四種情況,造成本地事務,與消息參與方的事務不一致。

  1. 事務參與方接收消息的可靠性。

消息中間件與事務參與方要確保能夠成功消費到消息。

  1. 消息重複消費

注意事務參與方的接口冪等性問題,消息參與方可能已經成功消費,由於網絡問題導致消息中間件認爲消息未消費,發起重試之後產生的問題。

解決方案

  1. 本地消息表

本地消息表的關鍵在於本地有一張存儲消息日誌的記錄表,需要啓動一個定時任務去不停地掃描消息日誌記錄,確保消息能夠被髮送。具體流程如下圖:

上圖流程:

1)事務發起方本地事務執行成功,在本地消息表中記錄消息日誌。
2)啓動定時任務,循環掃描本地消息表。
3)定時任務掃描到消息則發送消息到消息中間件。
4)消息中間件收到消息,成功返回消息發送成功通知給事務發起方。
5)事務發起方收到消息發送成功則刪除日誌消息。
6)事務參與方訂閱消息,消費消息。
7)事務參與方處理本地事務。
8)本地事務處理成功,發送成功 ack 給消息中間件。

需要注意的點:
事務參與方保證接口冪等性

  1. RocketMq 事務消息方案

Apache RocketMQ 4.3 之後的版本正式支持事務消息,爲分佈式事務實現提供了便利性支持。在 RocketMQ 4.3 後實現了完整的事務消息,實際上其實是對本地消息表的一個封裝,將本地消息表移動到了MQ內部,解決 Producer 端的消息發送與本地事務執行的原子性問題。

實現流程:

1)事務發起方發送Half事務消息
2)RocketMq 回覆Half發送成功
3)事務發起方執行本地事務
4)事務發起方執行本地事務成功,發送 commit 到 RocketMq,mq 投遞消息到事務參與方;事務發起方執行本地事務失敗,發送rollback到 RocketMq,mq 刪除消息。
5)當 RocketMq 一定時間內未收到來自事務發起方的確認信息,會對事務發起方進行事務回查
6)事務發起方查詢本地事務狀態。
7)事務發起方根據查詢到的事務狀態發送commint/rollback到 RocketMq。
8)當 RocketMq 發起commit後,收到失敗或一定時間未收到成功 ack,則會發起重試。

優點

消息數據獨立存儲,降低業務系統與消息系統之間的耦合。
吞吐量優於本地消息表方案。

缺點

一次消息發送需要兩次網絡請求 (half 消息 + commit/rollback)。
需要實現消息回查接口。

其實每種分佈式事務的解決方案都有優劣,我們需要權衡利弊,選擇最合適業務場景的一種纔是王道!


好了。今天就說到這了,我還會不斷分享自己的所學所想,希望我們一起走在成功的道路上!

不斷輸出乾貨的 Java 技術公衆號:狼王編程。公衆號內有大量的技術文章、海量視頻資源、精美腦圖,不妨來關注一下!回覆資料領取大量學習資源和免費書籍!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/JN_3AD3mv8N0GK7pJpLnIw