前面我們總結了broker端的核心參數,一些服務端原理細節後面文章再聊。本文我們重點討論Producer端的消息發送機制,希望通過本文我們能整體掌握Producer端的原理。
1、Producer架構
一圖勝千言,這裏筆者畫了一張Producer端消息發送的基本流程,如下圖:
總的來說,Kafka生產端發送數據過程涉及到序列化器Serializer、分區器Partitioner,消息緩存池Accumulator,還可能會涉及到攔截器Interceptor(這部分暫不做介紹)。由於篇幅有限這裏儘量做到言簡意賅。
2、客戶端與數據結構
2.1 新舊Producer
Kafka 0.8.2引入了新版本Producer客戶端,並自0.9.0版本開始穩定並建議生產使用,新版本Producer是o.a.k.clients.producer.KafkaProducer,見:
//新版本Producer
org.apache.kafka.clients.producer.KafkaProducer<K,V>
//舊版本Producer
kafka.javaapi.producer.Producer<K,V>
與舊版本相比,新版本Producer有點不同,一是連接Kafka方式上,舊版本連接的是Zookeeper,而新版本Producer連接的則是Broker;二是新版本Producer採用異步方式發送消息,與之前同步發送消息相對性能上大幅提升。
2.2 消息數據結構
Kafka將一條待發送的消息抽象爲ProducerRecord對象,其數據結構是:
public class ProducerRecord<K, V> {
private final String topic; //目標topic
private final Integer partition; //目標partition
private final Headers headers;//消息頭信息
private final K key; //消息key
private final V value; //消息體
private final Long timestamp; //消息時間戳
//省略構造方法與成員方法
}
目前消息結構包括6個核心屬性,分別是topic,partition,headers,key,value與timestamp,各屬性含義如上也比較好理解,其中headers屬性是Kafka 0.11.x 版本引入的,可以用它存儲一些應用或業務相關的信息。
3、序列化機制
3.1 序列化與反序列化
Kafka遵守生產者消費者模式,這中間涉及到序列化與反序列化。Producer發送消息要通過序列化器(Serializer)將消息對象轉換成字節數組,才能通過網絡傳輸到服務端,消費端則需要通過反序列化器(Deserializer)從服務端拉取字節數組轉成消息對象。可以用下圖表示:
生產端使用序列化器的方式非常簡單,只要指定key.serializer與value.serializer即可,如下示例:
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
消費端使用的反序列化器要和生產端的序列化器要一一對應,否則將無法解析出想要的數據。
3.2 默認序列化器
目前Kafka提供了十幾張序列化器,常見的序列化器有:
ByteArraySerializer // 序列化Byte數組,本質上什麼都不用做。
ByteBufferSerializer // 序列化ByteBuffer。
BytesSerializer // 序列化Kafka自定義的Bytes類。
StringSerializer // 序列化String類型。
LongSerializer // 序列化Long類型。
IntegerSerializer // 序列化Integer類型。
ShortSerializer // 序列化Short類型。
DoubleSerializer // 序列化Double類型。
FloatSerializer // 序列化Float類型。
除了使用Kafka自帶的序列化器,我們也可以自定義序列化器,只要實現Kafka Serializer接口並實現序列化邏輯即可,不過一般自帶序列化器已經夠用了。
4、消息分區機制
4.1 Topic分區
首先簡單說一下分區的概念,可以用下圖表示:
分區即partition是Kafka中非常重要的概念,分區的作用主要是爲Kafka提供負載均衡的能力,同時也是Kafka高吞吐量的保證。生產端要將數據發送到具體topic的某一個分區中,並且消息只在分區內有序。
4.2 分區器
消息通過send方法發送過程中,可能會經過分區器(Partitioner)的作用才能發往broker端。如果消息ProducerRecord中指定了partition字段,那麼就不需要分區器的作用,因爲partition代表的就是所要發往的分區號。
Kafka提供了默認分區器o.a.k.clients.producer.internals.DefaultPartitioner,並通過其partition()定義主要的分區分配邏輯。接下來我們看一下Kafka相關的分區策略。
4.3 分區策略
所謂分區策略就是決定消息發往具體分區所採用的算法或邏輯。目前Kafka主要提供兩種分區策略:哈希策略與輪詢策略。
當沒有爲消息指定key即key爲null時,消息會以輪詢的方式發送到各個分區(各個版本實現可能不一樣,還有一種隨機策略,有待考證);當key不爲null時,默認分區器會使用key的哈希值(採用Murmur2Hash算法)對partition數量取模,決定要把消息發送到哪個partition上。
5、消息緩衝池
5.1 緩存池介紹
生產端ProducerRecord經過序列化器、分區器處理後,並不是直接發往broker端,而是發送到客戶端的消息緩衝池(Accumulator) 中,最後交由Sender線程發往broker端。
緩衝池最大大小由參數buffer.memory控制,默認是32M,當生產消息的速度過快導致buffer滿了的時候,將阻塞max.block.ms時間,超時拋異常,所以buffer的大小可以根據實際的業務情況進行適當調整。
5.2 批量發送
發送到緩衝池中消息將會被分爲一個一個的batch,分批次的發送到broker 端,批次大小由參數batch.size控制,默認16KB。這就意味着正常情況下消息會攢夠16KB時纔會批量發送到broker端,所以一般減小batch大小有利於降低消息延時,增加batch大小有利於提升吞吐量。
但是消息並不是必須要達到一個batch尺寸纔會批量發送到服務端呢,Producer端提供了另一個重要參數linger.ms,用來控制batch最大的空閒時間,超過該時間的batch也會被髮送到broker端。
6、總結
本文先是介紹了Producer客戶端與ProducerRecord數據結構,然後重點介紹了序列化器Serializer、分區器Partitioner以及消息緩衝池Accumulator的基本原理,由於篇幅有限,還有一些細節比如攔截器、Sender線程並未涉及到,後續我們再來討論。希望通過本文讀者可以對Producer端消息發送機制有一個比較整體的認識。
往期推薦 點擊標題可跳轉