消息中間件可靠性和冪等性

問題1:如何消息不丟失

如何保障消息中間件 100% 消息投遞成功?在kafka中可以通過ack參數配置實現:

  • 參數爲0:producer不等待ack,若beoker還未寫入就返回,會造成數據丟失。
  • 參數爲1: leader寫入成功後返回ack,不等待follower同步成功;
  • 參數爲-1:等待全部folleower落盤成功後返回。

ack機制並不能保證消息100%不丟失,原因如下:

如果我們生產者每發一條消息,都要MQ持久化到磁盤中,然後再發起ack或nack的回調。這樣的話是不是我們MQ的吞吐量很不高,因爲每次都要把消息持久化到磁盤中。 寫入磁盤這個動作是很慢的。這個在高併發場景下是不能夠接受的,吞吐量太低了。

所以MQ持久化磁盤真實的實現,是通過異步調用處理的,他是有一定的機制,如:等到有幾千條消息的時候,會一次性的刷盤到磁盤上面。而不是每來一條消息,就刷盤一次。

所以comfirm機制其實是一個異步監聽的機制 ,是爲了保證系統的高吞吐量 ,這樣就導致了還是不能夠100%保障消息不丟失,因爲即使加上了confirm機制,消息在MQ內存中還沒有刷盤到磁盤就宕機了,還是沒法處理。

解決辦法:消息提前持久化 + 定時任務

流程說明:

  1. 訂單服務生產者再投遞消息之前,先把消息持久化到Redis或DB中,建議Redis,高性能。消息的狀態爲發送中。
  2. confirm機制監聽消息是否發送成功?如ack成功消息,刪除Redis中此消息。
  3. 如果nack不成功的消息,這個可以根據自身的業務選擇是否重發此消息。也可以刪除此消息,由自己的業務決定。
  4. 這邊加了個定時任務,來拉取隔一定時間了,消息狀態還是爲發送中的,這個狀態就表明,訂單服務是沒有收到ack成功消息。
  5. 定時任務會作補償性的投遞消息。這個時候如果MQ回調ack成功接收了,再把Redis中此消息刪除。

問題2:如何保證消息沒有重複發送?

爲了實現Producer的冪等性,Kafka引入了Producer ID(即PID)和Sequence Number。

  • PID。每個新的Producer在初始化的時候會被分配一個唯一的PID,這個PID對用戶是不可見的。
  • Sequence Numbler。(對於每個PID,該Producer發送數據的每個<Topic, Partition>都對應一個從0開始單調遞增的Sequence Number

Kafka可能存在多個生產者,會同時產生消息,但對Kafka來說,只需要保證每個生產者內部的消息冪等就可以了,所有引入了PID來標識不同的生產者。
對於Kafka來說,要解決的是生產者發送消息的冪等問題。也即需要區分每條消息是否重複。
Kafka通過爲每條消息增加一個Sequence Numbler,通過Sequence Numbler來區分每條消息。每條消息對應一個分區,不同的分區產生的消息不可能重複。所有Sequence Numbler對應每個分區
Broker端在緩存中保存了這seq number,對於接收的每條消息,如果其序號比Broker緩存中序號大於1則接受它,否則將其丟棄。這樣就可以實現了消息重複提交了。但是,只能保證單個Producer對於同一個<Topic, Partition>的Exactly Once語義。不能保證同一個Producer一個topic不同的partion冪等。

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