選單

Kafka工作原理和機制

Kafka工作流程

Kafka工作原理和機制

1。Kafka將訊息按Topic進行分類,每條message由三個屬性組成。

offset:表示message在當前Partition(分割槽)中的偏移量,是一個邏輯上的值,唯一確定了Partition中的一條message,可以簡單的認為是一個id;

MessageSize:表示message內容data的大小;

data:message的具體內容;

2。在整個kafka架構中,生產者和消費者採用釋出和訂閱的模式,生產者生產訊息,消費者消費訊息,它倆各司其職,並且都是面向topic的。(需要注意:topic是邏輯上的概念,而partition是物理上的概念,每個partition對應於一個log檔案,該log檔案中儲存的就是producer生產的資料)

3。Producer生產的資料會被不斷追加到該log檔案末端,且每條資料都有自己的offset。

4。消費者組中的每個消費者,都會實時記錄自己消費到了哪個offset,這樣當出現故障並恢復後,可從這個offset位置繼續進行消費,避免漏掉資料或者重複消費。

檔案儲存機制

檔案儲存結構及命名規則

在kafka的設計之初,考慮到了生產者生產的訊息不斷追加到log檔案末尾後導致log檔案過大的情況,所以採用了分片和索引機制,具體來說就是將每個partition分為多個segment。每個segment對應三個檔案:。index檔案、。log檔案、。timeindex檔案。其中。log和。index資料夾下,該資料夾的命名規則為:topic名稱+分割槽序號。

例如,csdn這個topic有2個分割槽,則其對應的資料夾為csdn-0,csdn-1;

如果我們開啟csdn-0這個資料夾,會看到裡面的檔案如下:

* 1

* 2

* 3

* 4

透過這個資料夾下有兩個log,我們可以得出結論,這個partition有2個segment。

檔案命名規則:partition全域性的第一個segment從0開始,後續每個segment檔名為上一個segment檔案最後一條訊息的offset值,數值大小為64位,20位數字字元長度,沒有數字用0填充。

注意:index 檔案並不是從0開始,也不是每次遞增1的,這是因為 Kafka採取稀疏索引儲存的方式,每隔一定位元組的資料建立一條索引,它減少了索引檔案大小,使得能夠把 index 對映到記憶體,降低了查詢時的磁碟 IO 開銷,同時也並沒有給查詢帶來太多的時間消耗。

下面引用一張舊的kafka儲存機制圖,不帶。timeindex 檔案:

檔案關係

index檔案和log檔案的關係:“。index”檔案儲存大量的索引資訊,“。log”檔案儲存大量的資料,索引檔案中的元資料指向對應資料檔案中message的物理偏移地址。

Kafka工作原理和機制

使用offset查詢message

因為每一個segment檔名為上一個Segment最後一條訊息的offset,所以當需要查詢一個指定offset的message時,透過在所有segment的檔名中進行二分查詢就能找到它歸屬的segment,再在其index檔案中找到其對應到檔案上的物理位置,就能拿出該message。

舉例:這裡我們以查詢offset為6的message為例,查詢流程如下:

首先要確定這個offset資訊在哪個segment檔案(由於是順序讀寫,這裡使用二分查詢法),第一個檔名為00000000000000000000,第二個為00000000000000150320,所以6這個offset的資料肯定在第一個檔案裡面;

找到檔案後,在這個檔案的 00000000000000000000。index檔案中的[6,9807]定位到00000000000000000000。log檔案中9807這個位置來進行資料讀取即可。

分割槽策略

為什麼要進行分割槽

在瞭解分割槽策略之前需要先了解為什麼要分割槽,可以從兩方面來解釋這個問題:

方便在叢集中擴充套件,每個Partition可以透過調整以適應它所在的機器,而一個topic又可以有多個Partition組成,因此整個叢集就可以適應任意大小的資料;

可以提高併發,分割槽後以Partition為單位讀寫。

分割槽策略

首先要知道producer傳送的資料其實需要封裝成一個ProducerRecord物件才可以,我們看ProducerRecord提供的方法如下:

Kafka工作原理和機制

透過這個構造方法,kafka分割槽策略有如下3種:

1。指明partition的情況下,直接將指明的值直接作為partition值;

2。沒有指明partition值但有key的情況下,將key的hash值與topic的partition數進行取餘得到 partition 值;

3。既沒有partition值又沒有key值的情況下,第一次呼叫時隨機生成一個整數,(後面每次呼叫在這個整數上自增),將這個值與 topic 可用的 partition 總數取餘得到 partition 值,也就是常說的 round-robin 演算法。

Kafka資料可靠性、故障處理

那麼kafka是怎麼保證資料可靠性的呢?怎麼保證exactly once的呢?在分散式的環境下又是如何進行故障處理的呢?

資料可靠性

首先我們要知道kafka傳送資料的機制:Kafka為了保證producer傳送的資料,能可靠的傳送到指定的topic,因此topic的每個partition收到producer傳送的資料後,都需要向producer傳送ack資訊(acknowledgement確認收到),如果producer收到ack,就會進行下一輪的傳送,否則重新發送資料。

副本資料同步策略

我們知道kafka的partition是主從結構的,因此當一個topic對應多個partiton時,為了保證leader掛掉之後,能在follower中選舉出新的leader且不丟失資料,就需要確保follower與leader同步完成之後,leader再發送ack。

Kafka工作原理和機制

這時會產生一個問題也就是副本資料同步策略:多少個follower同步完成之後才傳送ack呢?

有兩個方案對比如下:

半數以上完成同步,就傳送ack(優點:延遲低;缺點:選舉新的leader時,容忍n臺節點的故障,需要2n+1個副本)

全部完成同步,才傳送ack(優點:選舉新的leader時,容忍n臺節點的故障,需要n+1個副本;缺點:延遲高)

我們知道kafka採用零複製技術最佳化資料傳輸,因此網路延遲對kafka的影響較小。但是由於kafka一般都是處理海量資料,在同樣為了容忍n臺節點故障的前提下,第一種方案需要2n+1個副本,而第二種方案只需要n+1個副本,而Kafka的每個分割槽都有大量的資料,第一種方案會造成大量資料的冗餘,因此kafka採用了第二種方案:全部完成同步,才傳送ack。

ISR

kafka選用第二種發案來同步副本資料後,可能會出現一個問題:比如leader收到資料,然後開始向所有的follower同步資料,但是有那麼一個或多個follower因為掛掉了之類的原因出現了故障,不能和leader進行同步,那leader要一直等下去嗎?當然不可以,為了解決這個問題,引入了ISR的概念。

ISR是一個動態的in-sync replica set資料集,代表了和leader保持同步的follower集合。

相當於leader只要和ISR裡的follower進行資料同步就可以了,出現故障的會被ISR移出去,恢復之後並經過處理還會加入進來。那移出去的follower要經過怎樣的處理才能重新加入ISR呢?可以先思考一下,後面故障處理部分會進行分析。

ack應答機制

於資料的重要程度是不一樣的,有些可以少量允許丟失,希望快一點處理;有些不允許,希望穩妥一點處理,所以沒必要所有的資料處理的時候都等ISR中的follower全部接收成功。因此kafka處理資料時為了更加靈活,給使用者提供了三種可靠性級別,使用者可以透過調節acks引數來選擇合適的可靠性和延遲。

acks的引數分別可以配置為:0,1,-1。

它們的作用分別是:

配置為0:producer不等待broker的ack,這一操作提供了一個最低的延遲,broker一接收到還沒有寫入磁碟就已經返回,當broker故障時有可能丟失資料;

配置為1:producer等待broker的ack,partition的leader寫入磁碟成功後返回ack,但是如果在follower同步成功之前leader故障,那麼將會丟失資料;

配置為-1:producer等待broker的ack,partition的leader和follower全部寫入磁碟成功後才返回ack。但是如果在follower同步完成後,broker傳送ack之前,leader發生故障,此時會選舉新的leader,但是新的leader已經有了資料,但是由於沒有之前的ack,producer會再次傳送資料,那麼就會造成資料重複。

Exactly Once

冪等性機制

Kafka在0。11版本之後,引入了冪等性機制(idempotent),指的是當傳送同一條訊息時,資料在 Server 端只會被持久化一次,資料不丟不重,但是這裡的冪等性是有條件的:

只能保證 Producer 在單個會話內不丟不重,如果 Producer 出現意外掛掉再重啟是 無法保證的。因為冪等性情況下,是無法獲取之前的狀態資訊,因此是無法做到跨會話級別的不丟不重。

冪等性不能跨多個 Topic-Partition,只能保證單個 Partition 內的冪等性,當涉及多個Topic-Partition 時,這中間的狀態並沒有同步。

實現exactly once

一般對於重要的資料,我們需要實現資料的精確一致性,對於kafka也就是保證每條訊息被髮送且僅被髮送一次,不能重複,這就是exactly once。知道當acks = -1時,kafka可以實現at least once語義,這時候的資料會被至少傳送一次。再配合前面介紹的冪等性機制保證資料不重複,那合在一起就可以實現producer到broker的exactly once語義。

它們的關係可以寫成一個公式:idempotent + at least once = exactly once

那怎麼配置kafka以實現exactly once呢?

很簡單,只需將enable。idempotence屬性設定為true,kafka會自動將acks屬性設為-1。

故障處理

在分析故障處理之前,我們需要先知道幾個概念:

LEO:全稱Log End Offset,代表每個副本的最後一條訊息的offset

HW:全稱High Watermark,代表一個分割槽中所有副本最小的LEO,用來判定副本的備份進度,HW以外的訊息不被消費者可見。leader持有的HW即為分割槽的HW,同時leader所在broker還儲存了所有follower副本的LEO。

如下圖,是一個topic下的某一個partition裡的副本的LEO和HW關係:

Kafka工作原理和機制

注意:只有HW之前的資料才對Consumer可見,也就是隻有同一個分割槽下所有的副本都備份完成,才會讓Consumer消費。

它們之間的關係:leader的LEO >= follower的LEO >= leader儲存的follower的Leo >= leader的HW >= follower的HW

由於partition是實際的儲存資料的結構,因此kafka的故障主要分為兩類:follower故障和leader故障。

follower故障

問題:移出去的follower要經過怎樣的處理才能重新加入ISR呢?

透過前面我們已經知道follower發生故障後會被臨時踢出ISR,其實待該follower恢復後,follower會讀取本地磁碟記錄的上次的HW,並將log檔案高於HW的部分擷取掉,從HW開始向leader進行同步。等該follower的LEO大於等於該分割槽的HW(leader的HW),即follower追上leader之後(追上不代表相等),就可以重新加入ISR了。

leader故障

leader發生故障之後,會從ISR中選出一個新的leader,之後,為保證多個副本之間的資料一致性,其餘的follower會先將各自的log檔案高於HW的部分截掉,然後從新的leader同步資料。

注意:這隻能保證副本之間的資料一致性,並不能保證資料不丟失或者不重複。

那怎麼解決故障恢復後,資料丟失和重複的問題呢?

kafka在0。11版本引入了Lead Epoch來解決HW進行資料恢復時可能存在的資料丟失和重複的問題。

引入Lead Epoch

leader epoch實際是一對值(epoch, offset),epoch表示leader版本號,offset為對應版本leader的LEO,它在Leader Broker上單獨開闢了一組快取,來記錄(epoch, offset)這組鍵值對資料,這個鍵值對會被定期寫入一個檢查點檔案。Leader每發生一次變更epoch的值就會加1,offset就代表該epoch版本的Leader寫入的第一條日誌的位移。當Leader首次寫底層日誌時,會在快取中增加一個條目,否則不做更新。這樣就解決了之前版本使用HW進行資料恢復時可能存在的資料丟失和重複的問題

這就有點像HashMap原始碼裡面的modCount,用來記錄整體的更新變化。

kafka資料可靠性深度

kakfa的儲存機制、複製原理、同步原理、可靠性和永續性保證等等一步步對其可靠性進行分析,最後透過benchmark來增強對Kafka高可靠性的認知。

Kafka體系架構

Kafka工作原理和機制

如上圖所示,一個典型的Kafka體系架構包括若干Producer(可以是伺服器日誌,業務資料,頁面前端產生的page view等等),若干broker(Kafka支援水平擴充套件,一般broker數量越多,叢集吞吐率越高),若干Consumer (Group),以及一個Zookeeper叢集。Kafka透過Zookeeper管理叢集配置,選舉leader,以及在consumer group發生變化時進行rebalance。Producer使用push(推)模式將訊息釋出到broker,Consumer使用pull(拉)模式從broker訂閱並消費訊息。

名詞解釋:

——————END——————-