架構設計 | 基於訊息中介軟體,圖解柔性事務一致性

知了一笑發表於2020-09-08

本文原始碼:GitHub·點這裡 || GitEE·點這裡

一、最大努力通知

TCC分段提交適用分散式架構中對一致性、實時性要求較高的業務場景,在實際業務中也存在實時性比較低的業務,例如常見的簡訊通知,客戶端訊息,運營體系更新等業務,這時候為了減輕核心流程的複雜度和壓力,可以採取最大努力通知方式實現柔性事務的管理。

例如常見的第三方支付業務中,本地業務和支付端業務處理完成之後都會生成訊息通知,基本流程如下:

  • 本地業務預處理完成之後;
  • 請求第三方支付服務;
  • 支付操作成功對該賬號傳送訊息;
  • 支付服務回撥本地業務;
  • 本地業務生成系統通知訊息;

上述流程的訊息場景中有一些基礎特點,在核心業務處理完成之後,傳送訊息通知,允許失敗,在指定時間段內或者指定重試次數之後,允許訊息丟失情況存在,即訊息的不可靠性。

在實際的支付系統中,啟動每日對賬校驗時會對當日的流水做校驗,如果發現支付流水有未完成的流程,會有狀態彌補,後續可以繼續處理,這種手段在對賬中很常用。

二、可靠訊息

分散式事務基於可靠訊息最終一致性的實現方案,既然是可靠訊息,則要求MQ必須支援事務管理,這樣才能保證業務前後一致性。

1、RocketMQ事務訊息

RocketMQ在4.3版中開始支援分散式事務訊息,採用2PC的思想來實現了提交事務訊息,同時增加一個補償邏輯來處理二階段超時或者失敗的訊息,如下圖所示:

上圖說明了事務訊息的大致方案,其中分為兩個流程:正常事務訊息的傳送及提交、事務訊息的補償流程。

1.1 傳送及提交

(1)傳送訊息(half訊息,即傳送但不被消費);

(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中拉取訊息進行消費,根據生產者組獲取一個服務提供者傳送回查事務狀態請求,根據事務狀態來決定是提交或回滾訊息。

2、最終一致性

基於上述RocketMQ事務訊息可靠性的特點,即可以實現某類業務下事務的最終一致性。訊息傳送一致性是指產生訊息的業務動作與訊息傳送一致,也就是說如果業務操作成功,那麼由這個業務操作所產生的非同步訊息一定要傳送出去,否則就業務失敗回滾,訊息也會丟棄。

流程基本如下:

  • 傳送half事務訊息,無法被消費;
  • 本地業務程式碼邏輯處理完成;
  • 傳送確認訊息,標識該訊息可以消費;
  • 如果訊息生產方異常,取消整體動作;

該流程主要針對訊息生產方,在實際開發中,訊息的消費方也一樣很難處理,要保證最終一致性,必然會面對一個問題,消費方異常,訊息不斷的重試,可能存在部分業務處理成功,部分業務處理失敗的情況,這時候就要解決服務介面的冪等性問題。

三、冪等介面

1、冪等簡介

程式設計中一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。就是說,一次和多次請求某一個資源會產生同樣的作用影響。

在複雜的非同步流程中,尤其注意失敗重試問題,通常支付流程中,每次介面被請求,對每一步資料更新的操作,都會前置一步狀態查詢的流程,用來判斷下一步的資料更新是否該執行。

2、冪等介面

在系統服務介面請求中,任何明確的介面響應,例如失敗或成功,這樣業務流程都好處理,但是例如支付場景如果請求超時,如何判斷服務的結果狀態:客戶端請求超時,本地服務超時,請求支付超時,支付回撥超時,客戶端響應超時等,或者基於MQ的不斷重試機制,在部分業務異常狀態下,始終沒有返回成功,則訊息會一直重試。

這就需要設計流程化的狀態管理,尤其在訊息重試機制下,很少會再次對重試的業務介面使用重度的事務控制,有些業務被執行完畢,只需要判斷一個狀態,下次訊息重試跳過即可,只需要把未處理的業務補償處理即可,在重試機制下,在部分業務沒有全部執行成功之前,訊息會一直重試,直到最終全部完成。

四、原始碼地址

GitHub·地址
https://github.com/cicadasmile/data-manage-parent
GitEE·地址
https://gitee.com/cicadasmile/data-manage-parent

推薦閱讀:架構設計系列

序號 標題
01 架構設計:單服務.叢集.分散式,基本區別和聯絡
02 架構設計:分散式業務系統中,全域性ID生成策略
03 架構設計:分散式系統排程,Zookeeper叢集化管理
04 架構設計:介面冪等性原則,防重複提交Token管理
05 架構設計:快取管理模式,監控和記憶體回收策略
06 架構設計:非同步處理流程,多種實現模式詳解
07 架構設計:高併發流量削峰,共享資源加鎖機制
08 架構設計:分散式服務,庫表拆分模式詳解
09 架構設計:分散式事務①概念簡介和基礎理論
10 架構設計:基於電商交易流程,圖解TCC事務分段提交

相關文章