MQ系列11:如何保證訊息可靠性傳輸(除夕奉上)

Brand發表於2023-01-21

MQ系列1:訊息中介軟體執行原理
MQ系列2:訊息中介軟體的技術選型
MQ系列3:RocketMQ 架構分析
MQ系列4:NameServer 原理解析
MQ系列5:RocketMQ訊息的傳送模式
MQ系列6:訊息的消費
MQ系列7:訊息通訊,追求極致效能
MQ系列8:資料儲存,訊息佇列的高可用保障
MQ系列9:高可用架構分析
MQ系列10:如何保證訊息冪等性消費

1 介紹

這篇我們來說說 MQ 訊息的可靠性傳輸。可靠性傳輸其實包含兩種情況:一種是重複消費的情況,我們上一篇的冪等性消費解決的就是這個問題;另外一種是訊息丟失的情況的,要確保我們生產的訊息一定最終會得到消費。這時候就要從訊息執行的幾個階段去保證,每一個階段都不能出現問題。
image

2 訊息生產階段

訊息生產階段指的是訊息從生產到訊息傳送出去,經過網路傳輸,再到達Broker伺服器並被接收的這整個階段,我們需要一個健壯的確認機制(ACK)來保證訊息傳遞的可靠性。如果說訊息被接收到之後可以反饋給訊息生產方去確認,那這個過程就比較完美了。

  • 訊息建立和傳送事務性原則保證,要麼成功,要麼不成功
  • 同步傳送時,處理好返回值,如果發生異常,則進行異常捕捉並處理。
  • 非同步傳送時,處理好回撥的工作,如果發生異常,則進行異常捕捉並處理。
  • 異常/超時重試機制:如果長時間收不到確認返回結果,則需要進行重試;如果返回的結果是異常的,也可以有限的進行重試。
    超時重試和異常重試需要謹慎使用,重試次數也要謹慎斟酌。建議只對訊息丟失、錯誤、丟失特別敏感的時候使用,如果過度使用,反而可能造成請求堆積,佇列阻塞。
    image

3 訊息伺服器處理階段

Broker作為訊息伺服器,主要用於訊息收發的操作。一般情況下只要訊息服務正常執行,並依賴資料持久化能力,丟訊息的可能行就比較小。
但是在很多場景下,為了提升訊息佇列的效率,為了提升吞吐能力,在沒有確定完成持久化動作(刷盤)之前,就會把確認訊息返回。即只要訊息進行
Commit了,那就是成功的。但是如果還沒持久化成功便發生了當機,那就有存在訊息丟失的風險。可以參照如下最佳化:

  • 單節點模式下的Broker,最佳化Broker引數,在收到訊息並持久化到磁碟之後才把確認訊息返回給生產者 Producer。下面以RocketMQ為例子介紹配置最佳化手段:
    • 如果是RabbitMQ,則將Message的delivermode設定為2,exchange持久化動作操作完成之後才返回確認訊息,確保訊息不丟失;
    • 將 flushDiskType 設定為 SYNC_FLUSH,這是同步刷盤的意思,那就要求把這個動作同步完成之後才算訊息傳送成功。
  • 上面說的是單節點模式,如果配置了叢集模式,一般是多副本,則要求確認訊息要發到 一半以上(N/2 + 1)的節點並得到響應。這樣Producer才算真正傳送成功。
    image

4 訊息消費階段

訊息儲存到了Broker之後,剩下的就是訊息消費了。訊息消費階段跟生產階段大概一致,都是使用確認機制來保證訊息的可靠性和傳輸的。
當Consumer從Broker拉取到訊息之後,開始消費訊息,執行業務的的邏輯程式,業務程式執行成功後,才給Broker傳送消費確認響應。
如果沒成功或者訊息在傳送中途丟失,就沒有確認響應,這樣的話,在下一輪訊息拉取的時候,Broker依舊會返回這一條消費資料給你,避免網路抖動原因或者Consumer在執行消費出錯導致丟失。

4.1 消費分割槽的策略模式

多個消費者消費用一個分割槽,我們經常會出現這種情況:同一個Consumer Group 裡面有多個Consumer,比如Comsumer A 拉走了某一批資料,但是還沒返回確認訊息,Consumer B 又過來要 拉資料了,Broker要怎麼判定呢?
這邊舉個例子:Consumer A 拉取 index = 106 位置的資料,但是還沒返回消費完成的確認資訊,這時候消費位置依然是 index = 10086,如果 Consumer B 也過拉取資料,則

  • Broker接收確認資訊的時間未超時(比如配置為5s),則說明Consumer A還在消費中,回絕了Consumer B的請求。
  • Broker接收確認資訊的時間已超時(比如配置為5s),則說明Consumer A消費失敗了,返回 index = 106 位置的訊息資料給 Consumer B。
    所以,多個消費者消費同一個分割槽,要嚴格按照順序消費,具體可以參考官網的介紹,很詳細。

4.2 消費重試和死信佇列

在RocketMQ中,當訊息第一次消費失敗時,訊息佇列會自動進行訊息重試,達到最大重試次數(可配置閾值,比如5)後,若消費依然失敗,則表明消費者在正常情況下無法正確地消費該訊息。此時,訊息佇列RocketMQ版不會立刻將訊息丟棄,而是將其傳送到該消費者對應的特殊佇列中。這種無法被消費的訊息稱為死信訊息(Dead-Letter Message),儲存死信訊息的特殊佇列稱為死信佇列(Dead-Letter Queue)。
可以使用單獨的作業服務進行獨立處理,比如重新傳送死信訊息進行消費,避免訊息漏處理導致業務服務可用性問題。

image

5 總結

總得來說:MQ可以從三個角度來分析:生產者丟資料、訊息佇列伺服器(Broker)丟資料、消費者丟資料
生產者丟資料:RabbitMQ提供transaction和confirm模式來確保生產者不丟訊息。
訊息佇列服務丟資料:開啟持久化磁碟的配置。這個持久化配置可以和confirm機制配合使用,你可以在訊息持久化磁碟後,再給生產者傳送一個Ack訊號。
消費者丟資料:與生產者基本一直,等消費完成並接收到confirm才能確認是消費成功。超時或者失敗則重試,重試超過指定閾值的時候,計入死信佇列並獨立處理。

相關文章