天天看點

螞蟻金服:消息隊列事務型消息原理淺析

在金融級分布式架構的領域内,消息隊列是普遍被應用的異步通信産品,本文主要分為以下幾個小結,循序漸進的對消息隊列産品事務型消息設計原理進行分析和闡述:

  1. 消息隊列簡介
  2. 消息隊列應用執行個體
  3. 事務型消息設計方案
  4. 事務型消息總結

在分布式系統架構中,消息隊列的核心職責是為不同的應用系統提供異步通信服務,通常涉及以下三個重要角色:

螞蟻金服:消息隊列事務型消息原理淺析

消息釋出者,發送消息的應用系統,負責建立消息對象并通過網絡釋出到消息 Broker,釋出的過程一般是同步的。

螞蟻金服:消息隊列事務型消息原理淺析

消息 Broker,異步消息的“代理人”,負責接收并持久化消息,保證将消息投遞到指定的消息訂閱者應用系統。

螞蟻金服:消息隊列事務型消息原理淺析

消息訂閱者,訂閱消息的應用系統,負責消費消息 Broker 投遞過來的消息。

在分布式系統架構中,引入消息隊列帶來的三大核心優勢如下:

螞蟻金服:消息隊列事務型消息原理淺析

提高核心鍊路吞吐量

螞蟻金服:消息隊列事務型消息原理淺析

降低應用系統之間的耦合度

螞蟻金服:消息隊列事務型消息原理淺析

增強整體服務的高可用能力

分析和設計一個典型的支付應用業務邏輯,以 “賬單查詢 Case” 為例,基本業務邏輯如下:

螞蟻金服:消息隊列事務型消息原理淺析

檢索資料庫,擷取指定賬戶的賬單記錄。

螞蟻金服:消息隊列事務型消息原理淺析

記錄使用者的檢索行為,為風險控制提供資料積累。

螞蟻金服:消息隊列事務型消息原理淺析

發送短信到使用者手機,通知使用者其賬單被查詢事件。

依賴 “同步 RPC” 的設計方案 A 如下所示:

螞蟻金服:消息隊列事務型消息原理淺析

“賬單檢索 Case” 同步 RPC 設計方案 A

依賴 “異步消息隊列” 的設計方案 B 如下所示:

螞蟻金服:消息隊列事務型消息原理淺析

“賬單檢索 Case” 消息隊列設計方案 B

對比以上 A, B 兩個設計方案,引入消息隊列的設計方案 B 具有如下優勢:

螞蟻金服:消息隊列事務型消息原理淺析

“賬單服務” 處理 “賬單查詢 Case” 的耗時由 60 ms 縮減至 13 ms, 提高了服務的吞吐量。

螞蟻金服:消息隊列事務型消息原理淺析

“賬單服務” 和 “風險控制服務”、“短信通知服務” 完全解耦,如果在業務演進過程中,增加了新的下遊服務,“賬單服務” 完全無需變更。

螞蟻金服:消息隊列事務型消息原理淺析

當 “風險控制服務” 和 “短信通知服務” 不可用時,不會導緻 “賬單服務” 不可用。

通過以上 “賬單查詢 Case” 的設計方案,可以闡明引入消息隊列給分布式應用架構帶來的三大核心優勢。

下面繼續分析和設計 “賬單變更 Case”,基本業務邏輯如下:

螞蟻金服:消息隊列事務型消息原理淺析

寫入資料庫,變更指定賬戶的賬單記錄。

螞蟻金服:消息隊列事務型消息原理淺析

記錄使用者檢索行為,為風險控制提供資料積累。

螞蟻金服:消息隊列事務型消息原理淺析

發送短信到使用者手機,通知使用者其賬戶變更金額。

與 “賬單查詢 Case” 的差別在于資料庫操作是寫入,而不再是檢索。二者的主要差別是 “資料庫檢索” 不涉及資料庫事務,而 “資料庫寫入” 一定會涉及到資料庫事務,按照之前的引入消息隊列設計思路,“賬單變更 Case” 的設計方案 C 如下:

螞蟻金服:消息隊列事務型消息原理淺析

“賬單變更 Case” 消息隊列設計方案 C

“賬單變更 Case” 消息隊列設計方案 C 存在以下嚴重問題:

螞蟻金服:消息隊列事務型消息原理淺析

“賬單變更” 關聯的資料庫變更事務送出成功後,如果 “釋出賬單變更消息” 發送失敗(例如網絡異常或者消息隊列服務不可用),則會導緻 “記錄使用者行為” 和 “短信通知使用者” 後續動作失敗,無法完成風險控制資料積累,使用者也無法及時擷取到賬戶變更資訊。

為了解決以上設計方案 C 的嚴重問題,初步考慮先釋出 “賬單變更” 消息,再做資料庫變更的設計方案,但還是無法解決 “消息釋出” 和 “資料庫事務” 可能不一緻性的嚴重問題,如果消息已釋出成功過了,資料庫變更事務復原了,就會導緻使用者的賬單沒有變更,但使用者卻收到了賬戶變更短信,存在一緻性漏洞的 “賬單變更 Case” 消息隊列設計方案 D 如下所示:

螞蟻金服:消息隊列事務型消息原理淺析

“賬單變更 Case” 消息隊列設計方案 D

至此,可以梳理一下完美解決 “賬單變更 Case” 需要解決的關鍵點:

螞蟻金服:消息隊列事務型消息原理淺析

必須滿足“一緻性”要求,即賬單服務資料庫變更事務送出成功,風險控制服務和短信通知服務收到“賬單變更”消息;賬單服務資料庫變更事務復原,風險控制服務和短信通知服務不會收到“賬單變更”消息。

螞蟻金服:消息隊列事務型消息原理淺析

“賬單變更”消息釋出失敗,盡量避免導緻資料庫變更事務的復原。

事務性消息設計方案

為了解決以上描述的兩個需求,消息隊列需要提供一種特殊類型的消息:消息隊列收到消息後不會立刻投遞消息到消息訂閱者,而是根據消息釋出者應用的資料庫事務狀态決定消息是否投遞。如果資料庫事務送出,則消息投遞到訂閱者;反之則不投遞。此類消息被命名為 “事務型消息”。具體設計方案如下:

螞蟻金服:消息隊列事務型消息原理淺析

事務型消息設計方案 E

按照 “事務型消息設計方案 E” 的時序圖,消息釋出者和消息隊列之間增加了一個 “二階段” 消息,用來标明對應事務型消息的 “事務狀态”,消息隊列根據 “二階段” 消息決定是否投遞消息到下遊消息訂閱者。應用 “事務型消息”,“賬單變更 Case” 的可行解決方案如下所示:

螞蟻金服:消息隊列事務型消息原理淺析

“賬單變更 Case” 消息隊列-事務型消息設計方案 F

按照 “賬單變更 Case” 消息隊列-事務型消息設計方案 F,可以滿足“賬單服務資料庫變更”與“異步消息是否投遞到訂閱者應用”的事務一緻性需求。結合 Spring Framework 的事務模闆工具類僞代碼如下:

transactionTemplate.execute(new transactionCallback(){

@override

public Object doInTransaction(TransationStatus status){

try{

messageQueueSDK.publishTransactionMessage(message);

dbService.doUpdateOperation();

}catch (Exception e){

status.seRollbackOnly();

return null;

}

};

至于依據資料庫事務送出/復原狀态決定事務型 “二階段” 消息的發送,可以通過 Spring Framework 提供的事務模闆同步器自動感覺消息釋出者本地事務狀态,相關接口是:

org.springframework.transaction.support

TransactionSynchronizationManager.registerSyncronization(TransactionSyncronization)

至此,消息隊列 “事務型消息” 的設計方案和實作原理基本闡明清楚了,還遺留兩個可以深究的關鍵點:

  1. 為什麼消息釋出方法需要在本地資料庫事務方法之前?
  2. 如果消息隊列收不到事務型消息的二階段“送出 or 復原” 消息,如何處理?

針對第一個關鍵點,假定方法執行時序是先執行本地資料庫事務方法,之後釋出 “事務型” 消息,那麼消息釋出失敗會導緻消息釋出者本地事務復原,這明顯是不符合預期的,因為資料庫事務復原的成本比較消息釋出失敗是高昂的。

針對第二個關鍵點,在分布式網絡架構中是可能出現的,比如網絡異常、消息隊列服務短時間不可用等。這也是消息隊列提供嚴謹的 “事務型消息” 特性必須要解決的問題,如果消息隊列沒有收到 “送出 or 復原” 復原消息,則無法決定是否投遞消息到消息訂閱者,是以,嚴謹的 “事務型消息” 設計方案需要有一個異常場景,命名為 “事務型消息狀态回查”,具體設計方案如下:

螞蟻金服:消息隊列事務型消息原理淺析

事務型消息回查設計方案G

需要明确的是,“事務型消息狀态回查” 隻在 “送出 or 復原消息” 失敗的場景下被觸發,屬于異常路徑。

事務性消息總結

在分布式系統架構中,尤其是金融級業務應用的解決方案設計中,消息隊列提供 “事務型消息” 特性是必不可少的,“資料一緻性” 是金融級分布式架構的基本要求,本文通過執行個體逐漸說明消息隊列産品支援 “事務型消息” 的必要性、設計方案和原理,定義了明确的消息隊列事務型消息的核心原理:

螞蟻金服:消息隊列事務型消息原理淺析

消息隊列事務型消息基于 “二階段” 消息實作。

螞蟻金服:消息隊列事務型消息原理淺析

事務型消息是否投遞與消息釋出者本地事務狀态保持一緻。

螞蟻金服:消息隊列事務型消息原理淺析

事務型消息狀态回查是保證了 “事務型消息” 的嚴謹性。

原文釋出時間為:2018-05-22

本文來自雲栖社群合作夥伴“

中生代技術

”,了解相關資訊可以關注“

”。