Kafka 生產者概述

生產者:往消息隊列裏推送消息的應用

發送消息的過程

Kafka 生產者發送消息的過程:

  • Kafka 會將發送消息包裝爲 ProducerRecord 對象, ProducerRecord 對象包含了目標主題和要發送的內容,同時還可以指定鍵和分區。在發送 ProducerRecord 對象前,生產者會先把鍵和值對象序列化成字節數組,這樣它們才能夠在網絡上傳輸。
  • 接下來,數據被傳給分區器。如果之前已經在 ProducerRecord 對象裏指定了分區,那麼分區器就不會再做任何事情。如果沒有指定分區 ,那麼分區器會根據 ProducerRecord 對象的鍵來選擇一個分區,緊接着,這條記錄被添加到一個記錄批次裏,這個批次裏的所有消息會被髮送到相同的主題和分區上。有一個獨立的線程負責把這些記錄批次發送到相應的 broker 上。
  • 服務器在收到這些消息時會返回一個響應。如果消息成功寫入 Kafka,就返回一個 RecordMetaData 對象,它包含了主題和分區信息,以及記錄在分區裏的偏移量。如果寫入失敗,則會返回一個錯誤。生產者在收到錯誤之後會嘗試重新發送消息,如果達到指定的重試次數後還沒有成功,則直接拋出異常,不再重試。


發送方式

生產者有三種方式發送消息。實際上,發送的動作都是一致的,不由使用者決定。這三種方式區別在於對於消息是否正常到達的處理。

  • fire-and-forget:把消息發送給broker之後不關心其是否正常到達。大多數情況下,消息會正常到達,即使出錯了生產者也會自動重試。但是如果出錯了,對於我們的服務而言,是無感知的。這種適用於可丟失消息、對吞吐量要求大的場景,比如用戶點擊日誌上報。
  • 同步發送:我們使用send方法發送一條消息,它會返回一個Future,調用get方法可以阻塞住當前線程,等待返回。這種適用對消息可靠性要求高的場景,比如支付,要求消息不可丟失,如果丟失了則阻斷業務(或回滾)
  • 異步發送:使用send方法發送一條消息時指定回調函數,在broker返回結果時調用。這個回調函數可以進行錯誤日誌的記錄,或者重試。這種方式犧牲了一部分可靠性,但是吞吐量會比同步發送高很多。但是我們可以通過後續的補償操作彌補業務。

序列化器

一般而言,我們都會採用默認的序列化器。但是我們也可以採用更加高效和通用的序列化框架,比如Protobuf、Avro。
如果有業務需要,可以自定義序列化器實現消息的加密

分區

在kafka中,一個topic可以設定多個分區。在ProducerRecord中,我們可以指定分區,也可以通過指定key來決定消息被分到哪個分區。

如果key爲nil,那麼分區器會採用輪詢的方式將消息平均分配到各個分區;
如果key不爲nil,那麼分區器會採用內置的hash函數(不是java提供的,不會因爲java的升級而改變,可以保證新舊數據可以分到同一個分區)進行分區

指定key有兩個問題:

  • 必須保證分區可用。如果分區不可用,那麼消息將寫入失敗。這種情況很少發生,也可以通過複製和同步的功能來保證分區的高可用。
  • 使用默認的分區器,只有在不改變分區數量的情況下才能保證鍵與分區的映射。一般來說,我們最好是在創建分區時就把分區規劃好,避免在生產環境中修改分區數量。

自定義分區算法:如果需要保證key與分區的映射,同時也要兼顧後續業務增長增加分區數量,那麼最好是自定義分區算法,採用一致性hash進行分區。

順序保證
kafka可以保證同一個分區的消息時有序的,也就是說,broker會將同一個分區的消息按發送順序(準備來說是接收順序)寫入分區,消費者也會按照同樣的順序讀取。
但是爲了吞吐量,一般我們都會設置每次發送消息的數據大於1(max.in.flight.requests.per.connection)。如果AB兩條消息同一批次發送,正常來說應該A在B前面。但是有可能因爲A寫入出錯了,B成功了。之後A在下個批次重試,這時兩條消息在隊列裏的順序就會變成BA,消費也會按這個順序進行消費。
如果要確保消息順序,那麼在生產者的配置上,就應該將max.in.flight.requests.per.connection設置爲1,每次只提交一條消息,成功了再提交第二條消息。當然,這樣會嚴重影響吞吐量。
還有一種方法,那就是在發送AB的時候,先保證A發送成功,再發送B。B的發送可以同步發送A成功後再發送,或者在異步發送A消息的回調函數中進行。這種方法會加長單個業務的處理時長,但是發送消息的吞吐量不會受到影響。就整體而言,如果是有異步發送的方式,其吞吐量並不會下降多少。

參考

《kafka權威指南》

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