RocketMQ 原理:訊息儲存、高可用、訊息重試、訊息冪等性

語言: CN / TW / HK

目錄

訊息儲存

訊息儲存方式

非持久化

  1. 訊息生成者傳送訊息到 MQ

  2. MQ 返回 ACK(Acknowledge Character)給生產者

  3. MQ push 訊息給對應的消費者

  4. 訊息消費者返回 ACK 給 MQ

持久化

  1. 訊息生成者傳送訊息到 MQ

  2. MQ 收到訊息,將訊息進行持久化,儲存該訊息

  3. MQ 返回 ACK 給生產者

  4. MQ push 訊息給對應的消費者

  5. 訊息消費者返回 ACK 給 MQ

  6. MQ 刪除訊息

注意:

①第 5 步 MQ 在指定時間內接到訊息消費者返回 ACK,MQ 認定訊息消費成功,執行 6 。

②第 5 步 MQ 在指定時間內未接到訊息消費者返回 ACK,MQ 認定訊息消費失敗,重新執行 4、5、6 。

訊息儲存介質

資料庫

  • 實現:ActiveMQ

  • 缺點:資料庫瓶頸將成為 MQ 瓶頸

檔案系統

  • 實現:RocketMQ/Kafka/RabbitMQ

  • 解決方案:採用訊息刷盤機制進行資料儲存

  • 缺點:硬碟損壞的問題無法避免

訊息儲存與讀寫方式

SSD(Solid State Disk):固態硬碟

  • 隨機寫(100 KB/s)

  • 順序寫(600 M B/s):1秒1部電影

Linux 系統傳送資料的方式

  • “零拷貝”技術
    • 資料傳輸由傳統的 4 次複製簡化成 3 次複製,減少 1 次複製過程
    • Java 語言中使用 MappedByteBuffer 類實現了該技術
    • 要求:預留儲存空間,用於儲存資料(1G 儲存空間起步)

訊息儲存結構

如圖所示,MQ 資料儲存區域包含如下內容:

  • 訊息資料儲存區域
    • topic
    • queueId
    • message
  • 消費邏輯佇列
    • minOffset
    • maxOffset
    • consumerOffset
  • 索引
    • key 索引
    • 建立時間索引
    • ……

刷盤機制

同步刷盤

  1. 生產者傳送訊息到 MQ,MQ 接到訊息資料

  2. MQ 掛起生產者傳送訊息的執行緒

  3. MQ 將訊息資料寫入記憶體

  4. 記憶體資料寫入硬碟

  5. 磁碟儲存後返回 SUCCESS

  6. MQ 恢復掛起的生產者執行緒

  7. 傳送 ACK 到生產者

非同步刷盤

  1. 生產者傳送訊息到 MQ,MQ 接到訊息資料

  2. MQ 將訊息資料寫入記憶體

  3. 傳送 ACK 到生產者

小結

  • 同步刷盤 :安全性高,效率低,速度慢(適用於對資料安全要求較高的業務)
  • 非同步刷盤 :安全性低,效率高,速度快(適用於對資料處理速度要求較高的業務)
# 刷盤方式
#- ASYNC_FLUSH 非同步刷盤
#- SYNC_FLUSH 同步刷盤
flushDiskType=SYNC_FLUSH

高可用

高可用實現

nameserver

  • 無狀態 + 全伺服器註冊

訊息伺服器

  • 主從架構(2M-2S)

訊息生產

  • 生產者將相同的 topic 繫結到多個 group 組,保證即使 broker master 掛掉,其他 master 仍可正常進行訊息接收。

訊息消費

  • RocketMQ 自身會根據 broker master 的壓力確認是否由 master 承擔訊息讀取的功能,當 master 繁忙時候,自動切換由 slave 承擔資料讀取的工作。

主從複製

同步複製:

  • master 接到訊息後,先複製到 slave,然後反饋給生產者寫操作成功
  • 優點:資料安全,不丟資料,出現故障容易恢復
  • 缺點:影響資料吞吐量,整體效能低

非同步複製:

  • master 接到訊息後,立即返回給生產者寫操作成功,當訊息達到一定量後再非同步複製到slave
  • 優點:資料吞吐量大,操作延遲低,效能高
  • 缺點:資料不安全,會出現資料丟失的現象,一旦 master 出現故障,從上次資料同步到故障時間的資料將丟失

配置方式:

#Broker 的角色
#- ASYNC_MASTER 非同步複製Master
#- SYNC_MASTER 同步雙寫Master
#- SLAVE
brokerRole=SYNC_MASTER

負載均衡

Producer 負載均衡:

  • 內部實現了不同 broker 叢集中對同一 topic 對應訊息佇列的負載均衡

Consumer 兩種負載均衡策略:

  • 平均分配

  • 迴圈平均分配

訊息重試

當訊息消費後未正常返回消費成功的資訊將啟動訊息重試機制

兩種訊息重試機制:

  • 順序訊息重試

  • 無序訊息重試

順序訊息重試

  • 當消費者消費訊息失敗後,RocketMQ 會自動進行訊息重試(每次間隔時間為 1 秒)。
  • 注意:應用會出現訊息消費被阻塞的情況,因此,要對順序訊息的消費情況進行監控,避免阻塞現象的發生。

無序訊息重試

  • 無序訊息包括普通訊息、定時訊息、延時訊息、事務訊息。
  • 無序訊息重試僅適用於負載均衡(叢集)模型下的訊息消費,不適用於廣播模式下的訊息消費。
  • 為保障無序訊息的消費,MQ 設定了合理的訊息重試間隔時長。

死信佇列

概念:

  • 當訊息消費重試到達了指定次數(預設 16 次)後,MQ 將無法被正常消費的訊息稱為死信訊息(Dead-Letter Message)。

  • 死信訊息不會被直接拋棄,而是儲存到了一個全新的佇列中,該佇列稱為死信佇列(Dead-Letter Queue)。

死信佇列的特徵:

  • 歸屬某一個組(Gourp Id),而不歸屬 Topic,也不歸屬消費者。
  • 一個死信佇列中可以包含同一個組下的多個 Topic 中的死信訊息。
  • 死信佇列不會進行預設初始化,當第一個死信出現後,此佇列首次初始化。

死信佇列中的訊息的特徵:

  • 不會被再次重複消費。
  • 死信佇列中的訊息有效期為 3 天,達到時限後將被清除。

死信處理:

  • 在監控平臺中,通過查詢死信,獲取死信的 messageId,然後通過 id 對死信進行精準消費。

訊息冪等

訊息重複消費

訊息重複消費原因:

  • 生產者傳送了重複的訊息
    • 網路閃斷
    • 生產者宕機
  • 訊息伺服器投遞了重複的訊息
    • 網路閃斷
  • 動態的負載均衡過程
    • 網路閃斷/抖動
    • broker重啟
    • 訂閱方應用重啟(消費者)
    • 客戶端擴容
    • 客戶端縮容

訊息冪等

對同一條訊息,無論消費多少次,結果保持一致,稱為訊息冪等性。

解決方案:

  1. 使用業務 id 作為訊息的 key 。

  2. 在消費訊息時,客戶端對 key 做判定,未使用過放行,使用過拋棄。

  • 注意:messageId 由 RocketMQ 產生,messageId 並不具有唯一性,不能作用冪等判定條件。

常見的冪等方法示例:

  • 新增(不冪等):insert into order values(……)
  • 查詢(冪等)
  • 刪除(冪等):delete from 表 where id=1
  • 修改(不冪等):update account set balance = balance+100 where no=1
  • 修改(冪等):update account set balance = 100 where no=1