Kafka 訊息丟失與消費精確一次性
訊息丟失的場景
如果Kafka Producer使用“發後即忘”的方式傳送訊息,即呼叫producer.send(msg)方法來傳送訊息,方法會立即返回,但此時並不能說明訊息已經傳送成功。訊息傳送方式詳見初次邂逅Kafka生產者。
如果在訊息過程中發生了網路抖動,那麼訊息就會丟失;或傳送的訊息本身不符合要求,如大小超過Broker端的承受能力等(訊息太大的情況在生產中實際遇到過,最後透過在傳送前將訊息分包,再依次傳送,解決了該問題)。
解決該問題的方法就是:Producer要使用帶回撥通知的方法傳送訊息,即producer.send(msg, callback)。回撥方法callback可以告訴我們訊息是否真的提交成功了,一旦出現訊息傳送失敗的情況,可以使用程式碼進行容錯及補救。
例如:網路抖動導致的訊息丟失,可以使Producer重試;訊息不合格,則將訊息格式進行調整,再傳送。Producer使用帶回撥的訊息傳送API,可以及時發現訊息是否傳送失敗並作相應處理。
消費者丟失資料
Consumer端丟失資料主要體現在:拉取了訊息,並提交了消費位移,但是在訊息處理結束之前突然發生了當機等故障。消費者重生後,會從之前已提交的位移的下一個位置重新開始消費,之前未處理完成的訊息不會再次處理,即相當於消費者丟失了訊息。
解決Consumer端丟失訊息的方法也很簡單:將位移提交的時機改為訊息處理完成後,確認消費完成了一批訊息再提交相應的位移。這樣做,即使處理訊息的過程中發生了異常,由於沒有提交位移,下次消費時還會從上次的位移處重新拉取訊息,不會發生訊息丟失的情況。
具體的實現方法為,Consumer在消費訊息時,關閉自動提交位移,由應用程式手動提交位移。
Broker端丟失資料
Broker端丟失資料主要有以下幾種情況:
原來的Broker當機了,卻選舉了一個落後Leader太多的Broker成為新的Leader,那麼落後的這些訊息就都丟失了,可以禁止這些“unclean”的Broker競選成為Leader;
Kafka使用頁快取機制,將訊息寫入頁快取而非直接持久化至磁碟,將刷盤工作交由作業系統來排程,以此來保證高效率和高吞吐量。如果某一部分訊息還在記憶體頁中,未持久化至磁碟,此時Broker當機,重啟後則這部分訊息丟失,使用多副本機制可以避免Broker端丟失訊息;
避免訊息丟失的最佳實踐
不使用producer.send(msg),而使用帶回撥的producer.send(msg, callback)方法;
設定acks = all。acks引數是Producer的一個引數,代表了對訊息“已提交”的定義。如果設定成all,則表示所有的Broker副本都要接收到訊息,才算訊息“已提交”,是最高等級的“已提交”標準;
設定retries為一個較大的值,retries表示Producer傳送訊息失敗後的重試次數,如果發生了網路抖動等瞬時故障,可以透過重試機制重新傳送訊息,避免訊息丟失;
設定unclean.leader.election.enable = false。這是一個Broker端引數,表示哪些Broker有資格競選為分割槽的Leader。如果一個落後Leader太多的Follower所在Broker成為了新的Leader,則必然會導致訊息的丟失,故將該引數設定為false,即不允許這種情況的發生;
設定replication.factor >= 3。Broker端引數,表示每個分割槽的副本數大於等於3,使用冗餘的機制來防止訊息丟失;
設定min.insync.replicas > 1。Broker端引數,控制的是訊息至少被寫入多少個副本蔡栓是“已提交”,將該引數設定成大於1可以提升訊息永續性;
確保replication.factor > min.insync.replicas。若兩者相等,則如果有一個副本掛了,整個分割槽就無法正常工作了。推薦設定為:replication.factor = min.insync.replicas + 1;
確保訊息消費完再提交位移,將Consumer端引數enable.auto.commit設定為fasle,關閉位移自動提交,使用手動提交位移的形式。
精確一次消費
目前Kafka預設提供的訊息可靠機制是“至少一次”,即訊息不會丟失。上一節中我們知道,Producer如果傳送訊息失敗,則可以透過重試解決,若Broker端的應答未成功傳送給Producer(如網路抖動),Producer此時也會進行重試,再次傳送原來的訊息。這就是Kafka預設提供訊息至少一次性的原因,不過這可能會導致訊息重複傳送。
如果需要保證訊息消費的“最多一次”,那麼禁止Producer的重試即可。但是寫入失敗的訊息如果不重試則會永遠丟失。是否有其他方法來保證訊息的傳送既不丟失,也不重複消費?或者說即使Producer重複傳送了某些訊息,Broker端也能夠自動去重。
Kafka實際上透過兩種機制來確保訊息消費的精確一次:
冪等性(Idempotence)
事務(Transaction)
冪等性
所謂的冪等,簡單說就是對介面的多次呼叫所產生的結果和呼叫一次是一致的。在Kafka中,Producer預設不是冪等性的,Kafka於0.11.0.0版本引入該特性。設定引數enable.idempotence為true即可指定Producer的冪等性。開啟冪等生產者後,Kafka會自動進行訊息的去重傳送。為了實現生產者的冪等性,Kafka引入了producer id(後簡稱PID)和序列號(sequence number)兩個概念。
生產者例項在被建立的時候,會分配一個PID,這個PID對使用者完全透明。對於每個PID,訊息傳送到的每一個分割槽都有對應的序列號,這些序列號從0開始單調遞增。生產者每傳送一條訊息就會將**<PID, 分割槽>**對應的序列號值加1。
Broker端在記憶體中為每一對<PID, 分割槽>維護一個序列號SN_old。針對生產者傳送來的每一條訊息,對其序列號SN_new進行判斷,並作相應處理。
只有SN_new比SN_old大1時,即SN_new = SN_old + 1時,broker才會接受這條訊息;
SN_new < SN_old + 1,說明訊息被重複寫入,broker直接丟棄該條訊息;
SN_new > SN_old + 1,說明中間有資料尚未寫入,出現了訊息亂序,可能存在訊息丟失的現象,對應的生產者會丟擲OutOfOrderSequenceException。
注意:序列號針對<PID, 分割槽>,這意味著冪等生產者只能保證單個主題的單一分割槽內訊息不重複;其次,它只能實現單會話上的冪等性,不能實現跨會話的冪等性,這裡的會話即可以理解為:Producer程式的一次執行。當重啟了Producer程式之後,則冪等性保證就失效了。
事務
冪等性並不能跨多個分割槽運作,而Kafka事務則可以彌補這個缺陷。Kafka從0.11版本開始提供了對事務的支援,主要在read committed隔離級別。它能保證多條訊息原子性地寫入到目標分割槽,同時也能寶恆Consumer只能看到事務成功提交的訊息。
Producer端配置
事務型Producer能保證訊息原子性地寫入多個分割槽。批次的訊息要麼全部寫入成功,要麼全部失敗。並且,事務型Producer在重啟後,Kafka依然保證它們傳送訊息的精確一次處理。開啟事務型Producer的配置如下:
和冪等性Producer一樣,開啟enable.idempotence = true。
設定Producer端引數transcational.id。最好為其設定一個有意義的名字。
設定了事務型的Producer可以呼叫一些事務API,如下:initTransaction、beginTransaction、commitTransaction和abortTransaction,分別對應事務的初始化、事務開啟、事務提交和事務終止。
producer.initTransactions(); try { producer.beginTransaction(); producer.send(record1); producer.send(record2); producer.commitTransaction(); } catch (KafkaExecption e) { producer.abortTransaction(); }
上述程式碼中,事務型Producer可以保證record1和record2要麼全部提交成功,要麼全部寫入失敗。實際上,即使寫入失敗,Kafka也會將它們寫入到底層的日誌中,也就是說Consumer還是會看到這些訊息,具體Consumer端讀取事務型Producer傳送的訊息需要另行配置。
Consumer端配置
讀取事務型Producer傳送的訊息時,Consumer端的isolation.level參數列徵著事務的隔離級別,即決定了Consumer以怎樣的級別去讀取訊息。該引數有以下兩個取值:
read_uncommitted:預設值,表面Consumer能夠讀到Kafka寫入的任何訊息,不論事務型Producer是否正常提交了事務。顯然,如果啟用了事務型的Producer,則Consumer端引數就不要使用該值,否則事務是無效的。
read_committed:表面Consumer只會讀取事務型Producer成功提交的事務中寫入的訊息,同時,非事務型Producer寫入的所有訊息對Consumer也是可見的。
總結
Kafka所提供的訊息精確一次消費的手段有兩個:冪等性Producer和事務型Producer。
冪等性Producer只能保證單會話、單分割槽上的訊息冪等性;
事務型Producer可以保證跨分割槽、跨會話間的冪等性;
事務型Producer功能更為強大,但是同時,其效率也會比較低下。
本文來源於:
奈學開發者社群
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69976011/viewspace-2702388/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 解決RabbitMQ訊息丟失與重複消費問題MQ
- 實際業務處理 Kafka 訊息丟失、重複消費和順序消費的問題Kafka
- kafka消費者消費訊息的流程Kafka
- mq要如何處理訊息丟失、重複消費?MQ
- RabbitMq如何確保訊息不丟失MQ
- Kafka重複消費和丟失資料研究Kafka
- kafka 如何保證不重複消費又不丟失資料?Kafka
- Kafka中消費者延遲處理訊息Kafka
- Kafka訊息分發、主題分割槽與消費組的概念Kafka
- 訊息中介軟體—RocketMQ訊息消費(三)(訊息消費重試)MQ
- 阿里雲 KAFKA 消費者接收不到訊息阿里Kafka
- RabbitMQ防止訊息丟失MQ
- Kafka如何保證訊息不丟之無訊息丟失配置Kafka
- 伺服器當機會導致Kafka訊息丟失嗎伺服器Kafka
- 訊息中介軟體消費到的訊息處理失敗怎麼辦?
- Kafka 如何保證訊息消費的全域性順序性Kafka
- RocketMQ訊息丟失解決方案:事務訊息MQ
- 訊息推送平臺的實時數倉?!flink消費kafka訊息入到hiveKafkaHive
- Kafka之消費與心跳Kafka
- Kafka無法消費?!我的分散式訊息服務Kafka卻穩如泰山!Kafka分散式
- kafka 消費組功能驗證以及消費者資料重複資料丟失問題說明 3Kafka
- Pulsar VS. Kafka(1): 統一的訊息消費模型(Queue + Stream)Kafka模型
- RocketMQ -- 訊息消費過程MQ
- RocketMQ系列(三)訊息的生產與消費MQ
- RocketMQ -- 訊息消費佇列與索引檔案MQ佇列索引
- RocketMq訊息丟失問題解決MQ
- 《RabbitMQ》 | 訊息丟失也就這麼回事MQ
- Kafka消費與心跳機制Kafka
- kafka消費Kafka
- Apache Kafka訊息傳遞精確正好一次的含義 | TechMyTalkApacheKafka
- Spark streaming消費Kafka的正確姿勢SparkKafka
- 分散式訊息佇列:如何保證訊息不被重複消費?(訊息佇列消費的冪等性)分散式佇列
- 訊息佇列-如何保證訊息的不被重複消費(如何保證訊息消費的冪等性)佇列
- 訊息佇列——數十萬級訊息的消費方案佇列
- 如何處理RabbitMQ 訊息堆積和訊息丟失問題MQ
- RocketMQ的訊息是怎麼丟失的MQ
- RabbitMQ-如何保證訊息不丟失MQ
- RabbitMQ多消費者順序性消費訊息實現MQ