1. 程式人生 > >簡單聊聊訊息佇列的事務補償機制

簡單聊聊訊息佇列的事務補償機制

因為一直學習與嘗試負責公司的推送相關業務,包括整個應用的實現,其中就採用了基於訊息佇列的非同步事件驅動模型來做解耦非同步處理,所以就要去做了解一些相關的知識點,這邊稍作總結,並整理一下訊息補償機制的一套簡單實現的程式碼設計圖。

採用基於訊息佇列的非同步事件驅動模型來解決問題的時候,一個計較棘手的問題就是事務的一致性。

案例:現在使用者發起一個建立訂單的請求,如果我們是單系統架構,那麼修改訂單表,修改庫存表可能都是在同一個事務中完成,所以輕而易舉就達到了事物一致性原則,但是這不是我們要討論的,所以就帶過。現在微服務架構在網際網路公司大火特火,熱度未減,分散式是事務也成為了一個亟待解決的問題,阿里雲GTS標榜如何讓分散式事務更簡單。

比如,使用者發起一個建立訂單的請求,首先在訂單服務上生成了新的訂單,同時還要去庫存服務中減去庫存,因為是分散式架構,所以庫存扣減與訂單建立可能是在兩個遙遠的機器上,如果想要通過本地事務來解決那幾乎是不可能的,保證兩個事務之間的狀態一致性——訂單建立成功,庫存扣減失敗,如何回滾訂單?一直都是分散式架構中繞不開的挑戰。

分散式架構中如何解決事務問題,在很多技術群都上都在討論,比如dubbo , spring cloud等等。目前還沒有接觸到這方面相關知識,後續如果有幸參與,可做分享,本次想要聊的是假基於訊息佇列的非同步事件驅動是如何解決如上的分散式問題,以及如何保證事務一致性。

事務一致性原則(ACID):

  • Atomicity - 原子性,改變資料狀態要麼是一起完成,要麼一起失敗

  • Consistency - 一致性,資料的狀態是完整一致的

  • Isolation - 隔離線,即使有併發事務,互相之間也不影響

  • Durability - 永續性, 一旦事務提交,不可撤銷

訂單建立完成之後,傳送一個createOrderEvent到訊息佇列中,由訊息佇列負責轉發給訂閱該訊息的消費者進行處理。

好,這個時候如果訊息消費 成功,但是庫存不足,庫存扣減失敗,訂單建立則不能成功,這個時候很好處理,由庫存服務推送一個subInventoryFail給到訂單服務,訂單服務根據訊息將訂單轉為失敗狀態。

1、從使用者體驗的角度來說,整個過程是非同步的,所以對於使用者的體驗來說,就做不到“立馬成功或立馬失敗”的效果。

2、從技術的角度來說,整個過程你不再關注同一個事物的問題,而是關注最終訂單的狀態是否一致。【注:從分散式事務<-->最終一致性】保證事務最終一致性,但是基於這種事件驅動達到最終一致,解耦事務的成功實施需要依賴幾個因素。

a、訊息的投遞是否可靠。

b、訊息的可靠性,例如訂單服務已經成功建立訂單,但是還沒來得及傳送訊息就宕機或者各種原因,導致訂單的狀態不一致。

基於以上兩點的考慮,我們使用了一種基於本地事務的方案來保證訊息最終的一致性。

建立訂單與建立訊息事件都在本地事務中,屬於同一個事務,可以保證訂單表與訊息事件表的資料一致性。傳送訊息到訊息中介軟體,在事務提交之後傳送。到了庫存服務的時候,啟動一個定時任務去掃描訊息事件表,將未投遞失敗/消費 失敗的訊息進行消費,即補償事務一致性。

定時任務的方案可能不是最佳的,可以稍作改定,比如採用阿里巴巴開源的Canal

公司目前也是採用這種架構來解決訂單與庫存問題。有網友的做法是保證訊息投遞的可靠性,我們則是保證消費的一致性,具體的文章點我>>

可以將訊息佇列的進行封裝,做成了一個starter,程式碼設計上大致如圖下: