分佈式系統中的消息傳遞

1.消息隊列

在分佈式系統架構中,消息隊列的核心職責是爲不同的應用系統提供異步通信服務,通常涉及以下三個重要角色:
• 消息發佈者,發送消息的應用系統,負責創建消息對象並通過網絡發佈到消息Broker,發佈的過程一般是同步的。
• 消息Broker,異步消息的“代理人”,負責接收並持久化消息,保證將消息投遞到指定的消息訂閱者應用系統。
• 消息訂閱者,訂閱消息的應用系統,負責消費消息Broker投遞過來的消息。

在這裏插入圖片描述
異步消息隊列
在這裏插入圖片描述

  1. “賬單服務” 處理 “賬單查詢Case” 的耗時由 60 ms 縮減至 13 ms, 提高了服務的吞吐量。
  2. “賬單服務” 和 “風險控制服務”、“短信通知服務” 完全解耦,如果在業務演進過程中,增加了新的下游服務,“賬單服務” 完全無需變更。
  3. 當 “風險控制服務” 和 “短信通知服務” 不可用時,不會導致 “賬單服務” 不可用,
    通過以上 “賬單查詢Case” 的設計方案,可以闡明引入消息隊列給分佈式應用架構帶來的三大核心優勢。

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

  1. 寫入數據庫,變更指定賬戶的賬單記錄。
  2. 記錄用戶檢索行爲,爲風險控制提供數據積累。
  3. 發送短信到用戶手機,通知用戶其賬戶變更金額。
    與 “賬單查詢Case” 的區別在於數據庫操作是寫入,而不再是檢索。二者的主要區別是 “數據庫檢索” 不涉及數據庫事務,而 “數據庫寫入” 一定會涉及到數據庫事務,按照之前的引入消息隊列設計思路,“賬單變更Case” 的設計方案C如下:
    在這裏插入圖片描述
    “賬單變更Case” 消息隊列設計方案 C 存在以下嚴重問題:
    • “賬單變更” 關聯的數據庫變更事務提交成功後,如果 “發佈賬單變更消息” 發送失敗(例如網絡異常或者消息隊列服務不可用),則會導致 “記錄用戶行爲” 和 “短信通知用戶” 後續動作失敗,無法完成風險控制數據積累,用戶也無法及時獲取到賬戶變更信息。
    爲了解決以上設計方案 C 的嚴重問題,初步考慮先發布 “賬單變更” 消息,再做數據庫變更的設計方案,但還是無法解決 “消息發佈” 和 “數據庫事務” 可能不一致性的嚴重問題,如果消息已發佈成功過了,數據庫變更事務回滾了,就會導致用戶的賬單沒有變更,但用戶卻收到了賬戶變更短信,存在一致性漏洞的 “賬單變更Case” 消息隊列設計方案 D 如下所示
    在這裏插入圖片描述

2.事務型消息接收與投遞流程

事務型消息是否被投遞與發送端系統本地數據庫事務保持一致,如果本地數據庫事務提交則消息會被投遞給訂閱端;如果本地數據庫事務回滾,則直接丟棄消息不投遞給訂閱端系統。
事務型消息回查流程
事務型消息涉及典型的兩階段消息流程,第一階段是消息發佈端發送消息到可靠消息組件,第二階段是消息發佈端發送提交或者回滾指令到可靠消息組件,可靠消息組件根據此指令決定是否投遞消息到訂閱端系統。當第二階段指令出現異常時,可靠消息組件在一定時間後主動回查消息發送端系統,確認對應的事務型消息是否投遞

  • 事務型消息設計方案
    爲了解決以上描述的兩個需求,消息隊列需要提供一種特殊類型的消息:消息隊列收到消息後不會立刻投遞消息到消息訂閱者,而是根據消息發佈者應用的數據庫事務狀態決定消息是否投遞。如果數據庫事務提交,則消息投遞到訂閱者;反之則不投遞。此類消息被命名爲 “事務型消息”。具體設計方案如下:
    在這裏插入圖片描述
    按照 “事務型消息設計方案E” 的時序圖,消息發佈者和消息隊列之間增加了一個 “二階段” 消息,用來標明對應事務型消息的 “事務狀態”,消息隊列根據 “二階段” 消息決定是否投遞消息到下游消息訂閱者。應用 “事務型消息”,“賬單變更Case” 的可行解決方案如下所示:
    在這裏插入圖片描述
    至於依據數據庫事務提交/回滾狀態決定事務型 “二階段” 消息的發送,可以通過Spring Framework提供的事務模板同步器自動感知消息發佈者本地事務狀態,相關接口是:
    按照 “賬單變更Case” 消息隊列-事務型消息設計方案 ,可以滿足“賬單服務數據庫變更”與“異步消息是否投遞到訂閱者應用”的事務一致性需求。結合Spring Framework的事務模板工具類僞代碼如下:
transactionTemplate.execute(new TransactionCallback() {
    @Override
    public Object doInTransaction(TransactionStatus status) {
        try {
          messageQueueSDK.publishTransactionMessage(message);
          dbService.doUpdateOperation();
        } catch (Exception e) {
            status.setRollbackOnly();
        }
        return null;
    }
});

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

  • 爲什麼消息發佈方法需要在本地數據庫事務方法之前?
  • 如果消息隊列收不到事務型消息的二階段“提交 or 回滾” 消息,如何處理?
    在這裏插入圖片描述
    在分佈式系統架構中,消息隊列提供 “事務型消息” 特性是必不可少的,主要注意三個核心點:
  1. 消息隊列事務型消息基於 “二階段” 消息實現。
  2. 事務型消息是否投遞與消息發佈者本地事務狀態保持一致。
  3. 事務型消息狀態回查是保證了 “事務型消息” 的嚴謹性。

3.消息傳遞的推拉模式思考

• 推模式push:由消息中間件主動發消息給消費者
• 拉模式pull:消費者主動從消息中間件拉取消息
• 比較:採用push模式,可以儘可能快的把消息發給消費者,但是如果消費者處理一條消息能力較弱(處理時間長),消息中間件會不斷的發消息給消費者,到時消費者的緩存區溢出;採用pull模式,可能會增加消息的延遲。
• 消息訂閱者收到的消息不保證有序,即收到消息的順序與發佈者發送消息的順序可能會不一致
• 消息投遞策略爲至少一次,即對於同一條消息消息訂閱者可能收到多次,要求訂閱者保證冪等特性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章