Kakfa Producer 乾貨篇

Kafka Producer 是負責向 Kafka 服務發送消息的應用程序,該文不講故事,跟多偏重於原理和思考方面,對於 Kafka 沒有任何瞭解的人,可能有點困難和枯燥。

KafkaProducer

KafkaProducer 線程安全,支持多個線程共享同一個實例對象。

ProducerRecord

public class ProducerRecord<K, V> {
    private final String topic;
    private final Integer partition;
    private final Headers headers;
    private final K key;
    private final V value;
    private final Long timestamp;
}

timestamp 是指消息的時間戳,它有 CreateTime 和 LogAppendTime 兩種類型,前者表示消息創建的時間,後者表示消息追加到日誌文件的時間。

如果爲空的消息,稱爲墓碑消息。

header 消息頭部有什麼價值?

消息合法性的認證、value 數據一般都是規定數據格式,而 header 可以進行多變,實現不同的個性化需求。header 數據是不會被序列化的。

消息的發送

發送消息的三種模式:

  • 發後即忘
  • 同步
  • 異步

同步的方式可靠性最高,但性能方面會差很多,需要阻塞等待一條消息發送完之後,才能發送下一條消息。

異步方式,Kafka 提供了 Callback 方式來保證數據發送之後的響應操作,而且回調函數的調用也可以保證分區有序。

發送消息之後的返回值 RecordMetadata 對象

public final class RecordMetadata {   
   private final long offset;
    private final long timestamp;
    private final int serializedKeySize;
    private final int serializedValueSize;
    private final TopicPartition topicPartition;
    private volatile Long checksum;
}

timestamp 返回時已經是 LogAppendTime。

KafkaProducer.send() 方法的返回值是一個 Future 類型的對象,其提供了一個時間參數,用於實現可超時的阻塞。如果超時了,會拋異常。

異常處理

KafkaProducer 分爲兩種類型的異常:可重試的異常和不可重試異常。

可重試的異常:NetworkException、LeaderNotAvailableException

不可重試異常:RecordTooLargeException

出現異常之後,對於可重試異常,可以通過重試的方式進行處理,Kafka 提供了相應的參數

props.put(ProducerConfig.RETRIES_CONFIG,10);

整體架構

IMG_8120

整個生產者客戶端有兩個線程協調運行,這兩個線程分別爲主線程和 Sender 線程。

攔截器

生產者攔截器可以用來根據某些規則對數據進行過濾操作或者前置操作。

自定義攔截器通過實現 ProducerInterceptor 接口,

public interface ProducerInterceptor<K, V> extends Configurable {
public ProducerRecord<K, V> onSend(ProducerRecord<K, V> record);
public void onAcknowledgement(RecordMetadata metadata, Exception exception);
 public void close();
}

KafkaProducer 會在消息被響應之前或消息發送失敗時調用生產者攔截器的 onAcknowledgement 方法,優先於用戶設定的 Callback 之前執行。

加載攔截器

通過 interceptor.classes 配置項進行配置,支持多個攔截器,使用逗號進行分隔。

分區器

分區器確定消息往哪裏發。

  • 如果消息 ProducerRecord 中指定了 partition 字段,那麼就不需要分區器的作用了。
  • 默認分區器會對 key 進行哈希(MurmurHash2算法,高性能和低碰撞率),這裏的分區類型爲 AR,因而可能存在發送數據失敗重試的可能。
  • 如果 key 爲 null,那麼消息會通過輪詢的方式發往主體內的各個可用的分區(ISR)
  • 如果出現分區數量的增加的情況(Kafka 支持創建主題之後,新增分區,減少分區不支持),就很難保證key與分區之間的映射關係了。

RecordAccumulator 消息累加器

用來緩存消息以便 Sender 線程可以批量發送,進而減少網絡傳輸的資源損耗以提升性能。默認緩存大小由 buffer.memory 進行設定,默認值爲 32M。

如果生產者發送消息的速度超過發送到服務器的速度,則會導致生產者空間不足,在調用 send() 方法時阻塞或者拋異常,具體根據 max.block.ms 設置決定,默認爲 60秒。

RecordAccumulator 數據結構爲雙端隊列(Deque<ProducerBatch>),從尾部追加,頭部獲取。

ConcurrentMap<TopicPartition, Deque<ProducerBatch>> 數據格式存儲。

ProducerBatch

這是 ProducerRecord 集合,是指一個消息批次,這樣做的好處是使字節的使用更加緊湊,減少網絡傳輸的資源損耗以提升性能。

linger.ms 爲消息發送停留時間,當到達該時間之後,就會往 Sender 線程發送數據。

batch.size 爲每個 ProducerBatch 理論最大限度(部分數據也會超過該值),當達到這個值,就會往 Sender 線程發送數據。

BufferPool

主要用於實現 ByteBuffer 的複用,已實現緩存的高效利用。

不過 BufferPool 只針對特定大小的 ByteBuffer 進行管理,其他大小的 ByteBuffer 不會進入 BufferPool 中緩存。

大小由 batch.size 參數指定,默認 16K

ProducerBatch 與 BufferPool 的關係

當一條消息流入 RecordAccumulator 時,會先尋找與消息分區的所對應的雙端對列,在這個雙端隊列的尾部獲取一個 ProducerBatch,如果其空間還可以允許當前數據的大小,則將其寫入該 ProducerBatch,如果不可以就新建一個 ProducerBatch ,再進行寫入操作,ProducerBatch 大小根據 batch.size 來,如果當前這條數據大小已經超過 batch.size ,則按照實際大小創建 ProducerBatch 對象,如果是按照 batch.size 創建的 ProducerBatch ,這樣在使用完這段內存區域之後,可以通過 BufferPool 的管理來進行復用,如果超過,則不會再被複用。

InFlightRequests

緩存了已經發出去但還未收到相應的請求。

數據格式爲 Map<NodeId, Deque<NetworkClient.InFlightRequest>>

通過 max.in.flight.requests.per.connection 參數可以限制每個連接最多的請求數,默認爲5個,最多隻能緩存5個未響應的請求。

如果出現堆積,就要查看是否存在網絡問題。

InFlightRequests 可以獲得 leastLoadedNode,即所有 Node 中負載最小的那一個,通過將數據導到 leastLoadedNode 節點可以儘可能的發送數據。

當等待請求響應時間超過 request.timeout.ms 設定值時,會執行重試機制。

元數據更新

元數據更新是通過 sender 線程完成,但主線程通過 synchronized 和 final 方式來保障數據同步。

更新的間隔時間根據 metadata.max.age.ms 來,默認爲 5分鐘。

當需要更新元數據時,會先挑選出 leastLoadedNode,然後向這個 Node 發送 MetadataRequest 請求來獲取具體的元數據信息。

小結

InFlightRequests 中的請求不僅僅是發送消息,還有獲取元數據的請求。

InFlightRequests 發送主題消息時,是根據主題 Partition 的 Leader 副本所在的 Node 發起請求。

參數說明

acks

Kafka提供三種消息確認機制(acks),通過屬性request.required.acks設置。

  • acks=0:生產者不用等待代理返回確認信息,而連續發送消息

  • acks=1:默認模式,生產者需要等待Leader副本已成功將消息寫入日誌文件中。一定程度上降低數據丟失的風險。在Leader副本宕機時,Follower副本沒有及時同步數據,之後代理也不會再向宕機的Leader進行數據的獲取,數據就出現了丟失

  • acks=-1:Leader副本和所有Follower列表中副本都完成數據的存儲纔會想生產者發生確認信息,即同步副本必須大於1,通過min.insync.replicas設置,當同步副本不足該配置值時,生產者會拋異常。該種模式會影響生產者的發送數據的速度以及吞吐量。

生產者配置說明

屬性值 默認值 描述
Message.send.max.retries 3 生產者在丟失該消息前進行重試次數
Retry.backoff.ms 100 檢測新的Leader是否已選舉出來,更新主題的MetaData之前生產者需要等待的時間
Queue.buffering.max.ms 1000 當到達該時間後消息開始批量發送,若異步模式下,同時配置了Batch.num.messages,則達到這兩個閥值之一都將開始批量發送消息
Queue.buffering.max.message 10000 在異步模式下,在生產者必須被阻塞或者數據必須丟失之前,可以緩存到隊列中的未發送的最大消息條數,即初始化消息隊列的長度
Batch.num.messages 200 在異步模式下每次批量發送消息的最大消息數
Request.timeout.ms 1500 當需要acks時,生產者等待代理應答的超時時間,若該時間範圍內沒有應答,則會發送錯誤到客戶端
Topic.metadata.refresh.interval.ms 5min 生產者定時請求更新主題元數據的時間間隔。若設置爲0,則在每個消息發送後都去請求更新數據
Client.id Console.producer 生產者指定的一個標識字段,在每次請求中包含該字段,用來追蹤調用,根據該字段在邏輯上可以確認是哪個應用發出的請求
Queue.enqueue.timeout.ms 2147483647 該值爲0,標識該隊列沒滿是直接入隊,滿了則立即丟棄,負數表示無條件阻塞且不丟棄,正數表示阻塞達到該值時長後拋出QueueFullException異常

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