消息系統通常由生產者(Producer)、消費者(consumer)和消息代理(broker)三大部分組成,生產者會將消息寫入消息代理、消費者會從消息代理中讀取消息。對於消息代理而言,生產者和消費者都屬於客戶端:生產者和消費者會發送客戶端請求給服務端,服務端的處理分別是存儲消息和獲取消息,最後服務端返回響應結果給客戶端。
kafka工作流程及文件存儲機制
kafka中消息是以topic進行分類的,生產者生產消息,消費者消費消息,都是面向topic的。
topic是邏輯上的概念,而partition是物理上的概念。每個partition對應一個log文件,該log文件中存儲的就是producer生產的數據。Producer生產的數據會被不斷追加到該log文件末端,且每條數據都有自己的offset。消費者組中的每個消費者,都會實時記錄自己消費到了哪個offset,以便出錯恢復時,從上次的位置繼續消費。
由於生產者生產的消息會不斷追加到 log 文件末尾,爲防止 log 文件過大導致數據定位
效率低下,Kafka 採取了分片和索引機制,將每個 partition 分爲多個 segment。每個 segment
對應兩個文件——“.index”文件和“.log”文件。這些文件位於一個文件夾下,該文件夾的命名
規則爲:topic 名稱+分區序號。例如,first 這個 topic 有三個分區,則其對應的文件夾爲 first-
0,first-1,first-2。
索引機制的添加是爲了便於查找。
index 和 log 文件以當前 segment 的第一條消息的 offset 命名。下圖爲 index 文件和 log
文件的結構示意圖。
“.index”文件存儲大量的索引信息,“.log”文件存儲大量的數據,索引文件中的元數據指向對應數據文件中 message 的物理偏移地址
kafka生產者
分區策略
如果沒有提前創建消息所屬的主題,默認情況下主題的分區數量只有一個。一個主題只有一個分區時,會導致同一個主題的所有消息都會保存到一個節點上。一般我們會提前創建主題,指定更多的分區數,這樣同一個主題的所有消息就會分散在不同的節點上。
- 分區的原因
1.方便在集羣中擴展,每個Partition可以通過調整以適應它所在的機器,而一個topic又可以有多個Partition組成,因此整個集羣就可以適應任意大小的數據了;
2.可以提高併發,因爲可以以Partition爲單位讀寫了。
- 分區的原則
我們需要將 producer 發送的數據封裝成一個 ProducerRecord 對象。
1.指明partition的情況下,直接將指明的值直接作爲partition值。
2.沒有指明partition值只有key的情況下,將key的hash值與topic的partition數進行取餘得到partition值;
3.既沒有partition值又沒有key值的情況下,第一次調用時隨機生成一個整數(後面每次調用在這個整數上自增),將這個值與topic可用的partition總數取餘得到partition值,也就是常說的round-robin算法。
數據可靠性的保證
爲保證 producer 發送的數據,能可靠的發送到指定的 topic,topic 的每個 partition 收到
producer 發送的數據後,都需要向 producer 發送 ack(acknowledgement 確認收到),如果
producer 收到 ack,就會進行下一輪的發送,否則重新發送數據。
副本數據同步策略
方案 | 優點 | 缺點 |
半數以上完成同步,就發送ack | 延遲低 | 選舉新的leader時,容忍n臺節點的故障,需要2n+1個副本 |
全部完成同步,才發送ack | 選舉新的leader,容忍n臺節點的故障,需要n+1個副本 | 延遲高 |
1.同樣爲了容忍 n 臺節點的故障,第一種方案需要 2n+1 個副本,而第二種方案只需要 n+1
個副本,而 Kafka 的每個分區都有大量的數據,第一種方案會造成大量數據的冗餘。
2.雖然第二種方案的網絡延遲會比較高,但網絡延遲對 Kafka 的影響較小。
ISR
採用第二種方案之後,設想以下情景:leader 收到數據,所有 follower 都開始同步數據,
但有一個 follower,因爲某種故障,遲遲不能與 leader 進行同步,那 leader 就要一直等下去,
直到它完成同步,才能發送 ack。這個問題怎麼解決呢?
Leader 維護了一個動態的 in-sync replica set (ISR),意爲和 leader 保持同步的 follower 集
合。當 ISR 中的 follower 完成數據的同步之後,leader 就會給 follower 發送 ack。如果 follower
長時間 未 向 leader 同 步 數 據 , 則 該 follower 將 被 踢 出 ISR , 該 時 間 閾 值 由replica.lag.time.max.ms 參數設定。Leader 發生故障之後,就會從 ISR 中選舉新的 leader。
ack應答機制
對於某些不太重要的數據,對數據的可靠性要求不是很高,能夠容忍數據的少量丟失,
所以沒必要等 ISR 中的 follower 全部接收成功。
所以 Kafka 爲用戶提供了三種可靠性級別,用戶根據對可靠性和延遲的要求進行權衡,
選擇以下的配置。
acks參數配置:
acks:
0:producer 不等待 broker 的 ack,這一操作提供了一個最低的延遲,broker 一接收到還
沒有寫入磁盤就已經返回,當 broker 故障時有可能丟失數據;
1:producer 等待 broker 的 ack,partition 的 leader 落盤成功後返回 ack,如果在 follower
同步成功之前 leader 故障,那麼將會丟失數據;
ack=1數據丟失案例:
-1(all):
producer 等待 broker 的 ack,partition 的 leader 和 follower 全部落盤成功後才 返回 ack。但是如果在 follower 同步完成後,broker 發送 ack 之前,leader 發生故障,那麼會
造成數據重複。
ack=-1數據重複案例:
故障處理細節
LEO:指的是每個副本最大的 offset;
HW:指的是消費者能見到的最大的 offset,ISR 隊列中最小的 LEO。
- Follow故障
follow發生故障hou會被臨時提出ISR,待該follower恢復後,follower會讀取本地磁盤記錄的上次的HW,並將log文件高於HW的部分截取掉,從HW開始向leader進行同步,等該follower的LEO大於該Partition的HW,即follower追上leader之後,就可以重新加入ISR了。
- leader故障
leader發生故障之後,會從ISR中重新選取一個新的leader,之後,爲保證多個副本之間的數據一致性,其餘的follower會先將各自的log文件高於HW部分截掉,然後從新的leader同步數據。
ps:這隻能保證副本之間的數據的一致性,並不能保證數據不丟失或者不重複。
Exacty Once語義
將服務器的ack級別設置爲-1,可以保證Producer到Server之間不會丟失數據,即At Least Once語義。相對的,將服務器ACK級別設置爲0,可以保證生產者每條消息只會被髮送一次,即At More Once語義。
At Least Once 可以保證數據不丟失,但是不能保證數據不重複;相對的,At More Once可以保證數據不重複,但是不能保證數據不丟失,即Exactly Once語義。在0.11版本以前的kafka,對此也無能爲力,只能保證數據的不丟失,再在下游消費者對數據做全局去重。對於多個下游應用的情況,每個都需要單獨做全局去重,這對性能造成了造成了很大的影響。
0.11版本的kafka,引入了一項重大特性:冪等性。所謂的冪等性就是指Producer不論向Server發送了多少次重複的數據,Server端都只會持久化一條。冪等性結合At Lease Once語義,就構成了Kafka的Exactly Once語義。即:
At Least Once+冪等性=Exactly Once
需啓動冪等性,只需要將Producer的參數中enable.idompotence設置爲true即可。Kafka的冪等性實現其實就是將原來的下游需要做的去重放在了數據上游。開啓冪等性的Producer在初始化的時候會被分配PID,發往同一Partition的消息會附帶Sequence Number。而Broker端會對<PID,Partition,SeqNumber>做緩存,當具體相同主鍵的消息提交時,Broker只會持久化一條。
但是PID重啓就會變化,同時不同的Partition也具有不同主鍵,所以冪等性無法保證跨分區跨會話的Exactly Once。