簡介: 分佈式事務基於可靠消息最終一致性的實現方案,既然是可靠消息,則要求MQ必須支持事務管理,這樣才能保證業務先後一致性。git
TCC分段提交適用分佈式架構中對一致性、實時性要求較高的業務場景,在實際業務中也存在實時性比較低的業務,例如常見的短信通知,客戶端消息,運營體系更新等業務,這時候爲了減輕核心流程的複雜度和壓力,能夠採起最大努力通知方式實現柔性事務的管理。github
例如常見的第三方支付業務中,本地業務和支付端業務處理完成以後都會生成消息通知,基本流程以下:編程
上述流程的消息場景中有一些基礎特色,在覈心業務處理完成以後,發送消息通知,容許失敗,在指定時間段內或者指定重試次數以後,容許消息丟失狀況存在,即消息的不可靠性。架構
在實際的支付系統中,啓動每日對帳校驗時會對當日的流水作校驗,若是發現支付流水有未完成的流程,會有狀態彌補,後續能夠繼續處理,這種手段在對帳中很經常使用。異步
分佈式事務基於可靠消息最終一致性的實現方案,既然是可靠消息,則要求MQ必須支持事務管理,這樣才能保證業務先後一致性。分佈式
RocketMQ在4.3版中開始支持分佈式事務消息,採用2PC的思想來實現了提交事務消息,同時增長一個補償邏輯來處理二階段超時或者失敗的消息,以下圖所示:spa
上圖說明了事務消息的大體方案,其中分爲兩個流程:正常事務消息的發送及提交、事務消息的補償流程。設計
1.1 發送及提交code
(1)發送消息(half消息,即發送但不被消費);blog
(2)服務端響應消息寫入結果;
(3)根據發送結果執行本地事務,若是寫入失敗,此時half消息對業務不可見,本地邏輯不執行;
(4) 根據本地事務狀態執行Commit或者Rollback(Commit操做生成消息索引,消息對消費者可見)
1.1 補償流程
(1)對沒有Commit/Rollback的事務消息(pending狀態的消息),從服務端發起一次「回查」;
(2)Producer收到回查消息,檢查回查消息對應的本地事務的狀態;
(3)根據本地事務狀態,從新Commit或者Rollback;
其中,補償階段用於解決消息Commit或者Rollback發生超時或者失敗的狀況。
1.3 設計原理
在RocketMQ事務消息的主要流程中,一階段的消息如何對用戶不可見。其中,事務消息相對普通消息最大的特色就是一階段發送的消息對用戶是不可見的。那麼,如何作到寫入消息可是對用戶不可見呢?RocketMQ事務消息的作法是:若是消息是half消息,將備份原消息的主題與消息消費隊列,而後改變主題爲RMQ_SYS_TRANS_HALF_TOPIC。因爲消費組未訂閱該主題,故消費端沒法消費half類型的消息,而後RocketMQ會開啓一個定時任務,從Topic爲RMQ_SYS_TRANS_HALF_TOPIC中拉取消息進行消費,根據生產者組獲取一個服務提供者發送回查事務狀態請求,根據事務狀態來決定是提交或回滾消息。
基於上述RocketMQ事務消息可靠性的特色,便可以實現某類業務下事務的最終一致性。消息發送一致性是指產生消息的業務動做與消息發送一致,也就是說若是業務操做成功,那麼由這個業務操做所產生的異步消息必定要發送出去,不然就業務失敗回滾,消息也會丟棄。
流程基本以下:
該流程主要針對消息生產方,在實際開發中,消息的消費方也同樣很難處理,要保證最終一致性,必然會面對一個問題,消費方異常,消息不斷的重試,可能存在部分業務處理成功,部分業務處理失敗的狀況,這時候就要解決服務接口的冪等性問題。
編程中一個冪等操做的特色是其任意屢次執行所產生的影響均與一次執行的影響相同。就是說,一次和屢次請求某一個資源會產生一樣的做用影響。
在複雜的異步流程中,尤爲注意失敗重試問題,一般支付流程中,每次接口被請求,對每一步數據更新的操做,都會前置一步狀態查詢的流程,用來判斷下一步的數據更新是否該執行。
在系統服務接口請求中,任何明確的接口響應,例如失敗或成功,這樣業務流程都好處理,可是例如支付場景若是請求超時,如何判斷服務的結果狀態:客戶端請求超時,本地服務超時,請求支付超時,支付回調超時,客戶端響應超時等,或者基於MQ的不斷重試機制,在部分業務異常狀態下,始終沒有返回成功,則消息會一直重試。
這就須要設計流程化的狀態管理,尤爲在消息重試機制下,不多會再次對重試的業務接口使用重度的事務控制,有些業務被執行完畢,只須要判斷一個狀態,下次消息重試跳過便可,只須要把未處理的業務補償處理便可,在重試機制下,在部分業務沒有所有執行成功以前,消息會一直重試,直到最終所有完成。
GitHub·地址 https://github.com/cicadasmile/data-manage-parent GitEE·地址 https://gitee.com/cicadasmile/data-manage-parent