一、大體可以從三方面來說:
分別從Producer傳送機制、Broker的持久化機制,以及消費者的offSet機制來最大程度保證訊息不易丟失
從Producer的視角來看:如果訊息未能正確的儲存在MQ中,或者消費者未能正確的消費到這條訊息,都是訊息丟失。
從Broker的視角來看:如果訊息已經存在Broker裡面了,如何保證不會丟失呢(宕機、磁碟崩潰)
從Consumer的視角來看:如果訊息已經完成持久化了,但是Consumer取了,但是未消費成功且沒有反饋,就是訊息丟失
從Producer分析:如何確保訊息正確的傳送到了Broker?
預設情況下,可以透過同步的方式阻塞式的傳送,check SendStatus,狀態是OK,表示訊息一定成功的投遞到了Broker,狀態超時或者失敗,則會觸發預設的2次重試。此方法的傳送結果,可能Broker儲存成功了,也可能沒成功
採取事務訊息的投遞方式,並不能保證訊息100%投遞成功到了Broker,但是如果訊息傳送Ack失敗的話,此訊息會儲存在CommitLog當中,但是對ConsumerQueue是不可見的。可以在日誌中檢視到這條異常的訊息,嚴格意義上來講,也並沒有完全丟失
RocketMQ支援 日誌的索引,如果一條訊息傳送之後超時,也可以透過查詢日誌的API,來check是否在Broker儲存成功
從Broker分析:如果確保接收到的訊息不會丟失?
訊息支援持久化到Commitlog裡面,即使宕機後重啟,未消費的訊息也是可以加載出來的
Broker自身支援同步刷盤、非同步刷盤的策略,可以保證接收到的訊息一定儲存在本地的記憶體中
Broker叢集支援 1主N從的策略,支援同步複製和非同步複製的方式,同步複製可以保證即使Master 磁碟崩潰,訊息仍然不會丟失
從Cunmser分析:如何確保拉取到的訊息被成功消費?
消費者可以根據自身的策略批次Pull訊息
Consumer自身維護一個持久化的offset(對應MessageQueue裡面的min offset),標記已經成功消費或者已經成功發回到broker的訊息下標
如果Consumer消費失敗,那麼它會把這個訊息發回給Broker,發回成功後,再更新自己的offset
如果Consumer消費失敗,發回給broker時,broker掛掉了,那麼Consumer會定時重試這個操作
如果Consumer和broker一起掛了,訊息也不會丟失,因為consumer 裡面的offset是定時持久化的,重啟之後,繼續拉取offset之前的訊息到本地
二、訊息重複
上面在解決訊息順序問題時,引入了一個新的問題,就是訊息重複。那麼RocketMQ是怎樣解決訊息重複的問題呢?還是“恰好”不解決。
造成訊息的重複的根本原因是:網路不可達。只要透過網路交換資料,就無法避免這個問題。所以解決這個問題的辦法就是不解決,轉而繞過這個問題。那麼問題就變成了:如果消費端收到兩條一樣的訊息,應該怎樣處理?
1、消費端處理訊息的業務邏輯保持冪等性
2、保證每條訊息都有唯一編號且保證訊息處理成功與去重表的日誌同時出現
第1條很好理解,只要保持冪等性,不管來多少條重複訊息,最後處理的結果都一樣。第2條原理就是利用一張日誌表來記錄已經處理成功的訊息的ID,如果新到的訊息ID已經在日誌表中,那麼就不再處理這條訊息。
我們可以看到第1條的解決方式,很明顯應該在消費端實現,不屬於訊息系統要實現的功能。第2條可以訊息系統實現,也可以業務端實現。正常情況下出現重複訊息的機率不一定大,且由訊息系統實現的話,肯定會對訊息系統的吞吐量和高可用有影響,所以最好還是由業務端自己處理訊息重複的問題,這也是RocketMQ不解決訊息重複的問題的原因。
RocketMQ不保證訊息不重複,如果你的業務需要保證嚴格的不重複訊息,需要你自己在業務端去重。
一、大體可以從三方面來說:
分別從Producer傳送機制、Broker的持久化機制,以及消費者的offSet機制來最大程度保證訊息不易丟失
從Producer的視角來看:如果訊息未能正確的儲存在MQ中,或者消費者未能正確的消費到這條訊息,都是訊息丟失。
從Broker的視角來看:如果訊息已經存在Broker裡面了,如何保證不會丟失呢(宕機、磁碟崩潰)
從Consumer的視角來看:如果訊息已經完成持久化了,但是Consumer取了,但是未消費成功且沒有反饋,就是訊息丟失
從Producer分析:如何確保訊息正確的傳送到了Broker?
預設情況下,可以透過同步的方式阻塞式的傳送,check SendStatus,狀態是OK,表示訊息一定成功的投遞到了Broker,狀態超時或者失敗,則會觸發預設的2次重試。此方法的傳送結果,可能Broker儲存成功了,也可能沒成功
採取事務訊息的投遞方式,並不能保證訊息100%投遞成功到了Broker,但是如果訊息傳送Ack失敗的話,此訊息會儲存在CommitLog當中,但是對ConsumerQueue是不可見的。可以在日誌中檢視到這條異常的訊息,嚴格意義上來講,也並沒有完全丟失
RocketMQ支援 日誌的索引,如果一條訊息傳送之後超時,也可以透過查詢日誌的API,來check是否在Broker儲存成功
從Broker分析:如果確保接收到的訊息不會丟失?
訊息支援持久化到Commitlog裡面,即使宕機後重啟,未消費的訊息也是可以加載出來的
Broker自身支援同步刷盤、非同步刷盤的策略,可以保證接收到的訊息一定儲存在本地的記憶體中
Broker叢集支援 1主N從的策略,支援同步複製和非同步複製的方式,同步複製可以保證即使Master 磁碟崩潰,訊息仍然不會丟失
從Cunmser分析:如何確保拉取到的訊息被成功消費?
消費者可以根據自身的策略批次Pull訊息
Consumer自身維護一個持久化的offset(對應MessageQueue裡面的min offset),標記已經成功消費或者已經成功發回到broker的訊息下標
如果Consumer消費失敗,那麼它會把這個訊息發回給Broker,發回成功後,再更新自己的offset
如果Consumer消費失敗,發回給broker時,broker掛掉了,那麼Consumer會定時重試這個操作
如果Consumer和broker一起掛了,訊息也不會丟失,因為consumer 裡面的offset是定時持久化的,重啟之後,繼續拉取offset之前的訊息到本地
二、訊息重複
上面在解決訊息順序問題時,引入了一個新的問題,就是訊息重複。那麼RocketMQ是怎樣解決訊息重複的問題呢?還是“恰好”不解決。
造成訊息的重複的根本原因是:網路不可達。只要透過網路交換資料,就無法避免這個問題。所以解決這個問題的辦法就是不解決,轉而繞過這個問題。那麼問題就變成了:如果消費端收到兩條一樣的訊息,應該怎樣處理?
1、消費端處理訊息的業務邏輯保持冪等性
2、保證每條訊息都有唯一編號且保證訊息處理成功與去重表的日誌同時出現
第1條很好理解,只要保持冪等性,不管來多少條重複訊息,最後處理的結果都一樣。第2條原理就是利用一張日誌表來記錄已經處理成功的訊息的ID,如果新到的訊息ID已經在日誌表中,那麼就不再處理這條訊息。
我們可以看到第1條的解決方式,很明顯應該在消費端實現,不屬於訊息系統要實現的功能。第2條可以訊息系統實現,也可以業務端實現。正常情況下出現重複訊息的機率不一定大,且由訊息系統實現的話,肯定會對訊息系統的吞吐量和高可用有影響,所以最好還是由業務端自己處理訊息重複的問題,這也是RocketMQ不解決訊息重複的問題的原因。
RocketMQ不保證訊息不重複,如果你的業務需要保證嚴格的不重複訊息,需要你自己在業務端去重。