本次kafka相關分析總結,以apache kafka爲準。
地址:http://kafka.apache.org/documentation/
中文文檔地址:https://kafka.apachecn.org/
瞭解kafka需要先了解以下幾個基本概念:
名稱 | 說明 |
---|---|
Broker | kafka作爲中件,幫我們存儲和轉發消息,所以我們把kafkar的服務叫做Broker。 |
Record(消息) | 客戶端之間傳輸的數據 |
Topic | 生產者與消費者關聯的途徑,可以理解爲其他消息中間件的隊列 |
Producer | 發送消息的生產者 |
Consumer | 接收消息的消費者 |
Partition與Cluster | topic分區 |
Segment | partition再做一個切分,切分出來的單位叫段(segment) |
Consumer Group | 消費組 |
Consumer Offset | 消費偏移量 |
Broker
Brokder :kafka做爲一箇中間件,是幫我們存儲和轉發消息的,它做的事有點像中介,所以我們把kafka的服務叫Broker。生產者和消費者都需要跟這個Broker建立一個連接,纔可以實現消息的收發。
消息
客戶端之間傳輸的數據叫做消息,或者叫記錄(Record)。 生產者對應的封裝類是ProducerRecord,消費者對應的封裝類是ConsumerRecord。
消息在服務端存儲的格式(Record Batch和Record)
Record Batch
baseOffset: int64
batchLength: int32
partitionLeaderEpoch: int32
magic: int8 (current magic value is 2)
crc: int32
attributes: int16
bit 0~2:
0: no compression
1: gzip
2: snappy
3: lz4
4: zstd
bit 3: timestampType
bit 4: isTransactional (0 means not transactional)
bit 5: isControlBatch (0 means not a control batch)
bit 6~15: unused
lastOffsetDelta: int32
firstTimestamp: int64
maxTimestamp: int64
producerId: int64
producerEpoch: int16
baseSequence: int32
records: [Record]
Control Batches
version: int16 (current version is 0)
type: int16 (0 indicates an abort marker, 1 indicates a commit)
Record
length: varint
attributes: int8
bit 0~7: unused
timestampDelta: varint
offsetDelta: varint
keyLength: varint
key: byte[]
valueLen: varint
value: byte[]
Headers => [Header]
Record Header
headerKeyLength: varint
headerKey: String
headerValueLength: varint
Value: byte[]
生產者
發送消息的一方叫做生產者,接收消息的一方叫做消費者。
爲了提升發送速率,生產者不是逐條發送消息給Broker,而是批量發送的。
參數名 | 默認值 | 說明 |
---|---|---|
batch.size | 16384(byte) | 多少條發送一次 |
linger.ms | 5(ms) | 批量發送的等待時間 |
acks | 1 | 0 發出去就確認、1 leader落盤就確認 、all所有Follower同步才完成 |
retries | 3 | 異常自動重試次數 |
buffer.memory | 33554432(32M) | 客戶端緩衝區大小,滿了也會觸發發送 |
max.block.ms | 3000(ms) | 獲取元數據時生產者的相親阻塞時間,超時後拋出異常 |
消費者
一般來說消費者有兩種消費模式,一種是pull模式,一種是push模式。
Pull模式就是消息放到Broker,消費者自己決定什麼時候去獲取。
Push模式是消息放在Consumer,只要消息到達Broker,都直接給消費者。
Kafka只有pull模式。 是因Push模式下,如果消息生產的速度遠遠大於消費者的速率,那消費者就會不堪重負,直到掛掉。而Pull模式,消費者可以自己控制一次獲取多少條消息。
參數名 | 默認值 | 說明 |
---|---|---|
max.poll.records | 500 | 一次獲取消息數 |
auto.commit.interval.ms | 1000(ms) | 消費者自動提交間隔時間 |
auto.offset.reset | earliest | 從最早的數據開始消費(earliest 、 latest、 none) |
Topic
生產者如何與消費者關聯起來?其他的消息中間件的關聯名叫隊列,也就是說,生產者發送消息,要指定發給哪個隊列。消費者接收消息,要指定從哪個隊列接收。
在kafka裏面,這個隊列叫Topic,是一個邏輯概念,可以理解爲一組消息的集合。
參數名 | 默認值 | 說明 |
---|---|---|
auto.create.topic.enable | true | 是否開啓默認創建Topic(生產環境建議關閉,手動控制) |
Partition 與 Cluster
如果一個Topic中的消息太多,會帶來兩個問題:
-
不方便橫向擴展,比如想在集羣中把數據分佈在不同的機器上實現擴展,而不是通過升級硬件做到,如果一個Topic的消息無法在物理上拆分到多臺機器的時候,這個是做不到的。
-
併發或負載問題,所有客戶端操作的都是一個Topic,在高併發場景下性能會大大下降。
如何解決這個問題?我們想到的就是把一個Topic進行拆分(分片思想)。
kafka引入了一個分區(Partition)的概念。一個Topic可以劃分成多個分區。
Partition思想上有點類似於分庫分表,實現的也是橫向擴展和負載的目的。
如:Topic有3個分區,生產者依次發送9條消息,對消息進行編號。
第一個分區 1、4、7,第二個分區2、5、8、第三個分區3、6、9,這個就實現了負載。
每個partition都有一個物理目錄。在配置的數據目錄下(日誌就是數據):
/tmp/kafka-logs/
test-topic-0
test-topic-1
與RabbitMQ不一樣的地方是,Partition裏面的消息被讀取後不會被刪除,所以同一批消息在一個Partition裏面順序、追加寫入的。這個也是kafka吞吐量大的一個很重要的原因。
Partition 副本 Replica機制
如果partition數據只存儲一份,在發生網絡或者硬件故障時,該分區的數據就無法訪問或者無法回覆了。
每個partition可以有若干個副本(Replica),副本必須在不同的Broker上面。一般我們說的副本包括其中的主節點。
舉例:部署了3個Broker,該Topic有3個分區,每個分區一共3個副本。
注意:這些存放相同數據的partition副本有Leader(圖中紅色)和follower(圖中綠色)的概念。Leader在哪臺機器是不一定的,是通過選舉算法選舉出來的。
生產者發消息、消費者讀消息都是針對leader,主要是爲一致性考慮,如Mysql中的主從同步會有一定的延遲問題。follower的數據是從leader同步過來的。
Segment
kafka的數據是放在後綴.log的文件裏的。如果一個partition只有一個log文件,消息不斷的追加,這個log文件也會越來越大,這個時候要檢索數據效率就很低了。
所以把partiton再做一個切分,切分出來的單位就叫做段(Segment)。kafka的存儲文件是劃分成段來存儲的。
默認存儲路徑:/tmp/kafka-logs/
每個segment都至少有1個數據文件和2個索引文件,這3個文件是成套出現的。
參數名 | 默認值 | 說明 |
---|---|---|
log.segment.bytes | 1G | 一個segment大小 |
Consumer Group
如果生產者產生消息的速度過快,會造成消息在Broker的堆積,影響Broker的性能。怎麼提升消息的消費速率呢?增加消費者的數量。但是這麼多消費者,怎麼知道大家是不是消費的同一個Topic呢?
所以引入了一個Consumer Group消費組的概念,在代碼中通過group id來配置。消費同一個topic的消費者不一定是同一個組,只有group id相同的消費者纔是同一個消費者組。
注意:同一個group中的消費者,不能消費相同的partition——partition要在消費者之間分配。
Consumer Offset
上邊我們說了,partition裏面的消息是順序寫入的,被讀取之後不會被刪除。
如果消費者掛了或者下一次讀取,想要接着上次的位置讀取消息,或者從某個特定的位置讀取消息,該怎麼辦呢?會不會出現重複消費的情況?
因爲消息是有序的,我們可以對消息進行編號,用來標識一條唯一的消息。
這個編號我們不把它叫做offset,偏移量。
offset記錄着下一條將要發送給consumer的消息的序號。
這個消費者跟partition之間的偏移量沒有保存在ZK,而是直接保存在服務端。
架構圖:
總覽: