1. 程式人生 > >Kafka底層原理剖析(近萬字建議收藏)

Kafka底層原理剖析(近萬字建議收藏)

## Kafka 簡介 Apache Kafka 是一個分散式釋出-訂閱訊息系統。是大資料領域訊息佇列中唯一的王者。最初由 linkedin 公司使用 scala 語言開發,在2010年貢獻給了Apache基金會併成為頂級開源專案。至今已有十餘年,仍然是大資料領域不可或缺的並且是越來越重要的一個元件。 Kafka 適合離線和線上訊息,訊息保留在磁碟上,並在叢集內複製以防止資料丟失。kafka構建在zookeeper同步服務之上。它與 Flink 和 Spark 有非常好的整合,應用於實時流式資料分析。 Kafka特點: 1. 可靠性:具有副本及容錯機制。 2. 可擴充套件性:kafka無需停機即可擴充套件節點及節點上線。 3. 永續性:資料儲存到磁碟上,永續性儲存。 4. 效能:kafka具有高吞吐量。達到TB級的資料,也有非常穩定的效能。 5. 速度快:順序寫入和零拷貝技術使得kafka延遲控制在毫秒級。 ## Kafka 底層原理 先看下 Kafka 系統的架構 ![Kafka 架構](https://cdn.jsdelivr.net/gh/sunmyuan/cdn/210115_1.png) kafka支援訊息持久化,消費端是主動拉取資料,消費狀態和訂閱關係由客戶端負責維護,**訊息消費完後,不會立即刪除,會保留歷史訊息**。因此支援多訂閱時,訊息只會儲存一份就可以。 1. **broker**:kafka叢集中包含一個或者多個服務例項(節點),這種服務例項被稱為broker(一個broker就是一個節點/一個伺服器); 2. **topic**:每條釋出到kafka叢集的訊息都屬於某個類別,這個類別就叫做topic; 3. **partition**:partition是一個物理上的概念,每個topic包含一個或者多個partition; 4. **segment**:一個partition當中存在多個segment檔案段,每個segment分為兩部分,.log檔案和 .index 檔案,其中 .index 檔案是索引檔案,主要用於快速查詢, .log 檔案當中資料的偏移量位置; 5. **producer**:訊息的生產者,負責釋出訊息到 kafka 的 broker 中; 6. **consumer**:訊息的消費者,向 kafka 的 broker 中讀取訊息的客戶端; 7. **consumer group**:消費者組,每一個 consumer 屬於一個特定的 consumer group(可以為每個consumer指定 groupName); 8. **.log**:存放資料檔案; 8. **.index**:存放.log檔案的索引資料。 ### Kafka 主要元件 #### 1. producer(生產者) producer主要是用於生產訊息,是kafka當中的訊息生產者,生產的訊息通過topic進行歸類,儲存到kafka的broker裡面去。 #### 2. topic(主題) 1. kafka將訊息以topic為單位進行歸類; 2. topic特指kafka處理的訊息源(feeds of messages)的不同分類; 3. topic是一種分類或者釋出的一些列記錄的名義上的名字。kafka主題始終是支援多使用者訂閱的;也就是說,一 個主題可以有零個,一個或者多個消費者訂閱寫入的資料; 4. 在kafka叢集中,可以有無數的主題; 5. 生產者和消費者消費資料一般以主題為單位。更細粒度可以到分割槽級別。 #### 3. partition(分割槽) kafka當中,topic是訊息的歸類,一個topic可以有多個分割槽(partition),每個分割槽儲存部分topic的資料,所有的partition當中的資料全部合併起來,就是一個topic當中的所有的資料。 一個broker服務下,可以建立多個分割槽,broker數與分割槽數沒有關係; 在kafka中,每一個分割槽會有一個編號:編號從0開始。 **每一個分割槽內的資料是有序的,但全域性的資料不能保證是有序的。**(有序是指生產什麼樣順序,消費時也是什麼樣的順序) #### 4. consumer(消費者) consumer是kafka當中的消費者,主要用於消費kafka當中的資料,消費者一定是歸屬於某個消費組中的。 #### 5. consumer group(消費者組) 消費者組由一個或者多個消費者組成,**同一個組中的消費者對於同一條訊息只消費一次**。 每個消費者都屬於某個消費者組,如果不指定,那麼所有的消費者都屬於預設的組。 每個消費者組都有一個ID,即group ID。組內的所有消費者協調在一起來消費一個訂閱主題( topic)的所有分割槽(partition)。當然,**每個分割槽只能由同一個消費組內的一個消費者(consumer)來消費**,可以由不同的**消費組**來消費。 **partition數量決定了每個consumer group中併發消費者的最大數量**。如下圖: ![示例 1](https://cdn.jsdelivr.net/gh/sunmyuan/cdn/210116_1.png) 如上面左圖所示,如果只有兩個分割槽,即使一個組內的消費者有4個,也會有兩個空閒的。 如上面右圖所示,有4個分割槽,每個消費者消費一個分割槽,併發量達到最大4。 在來看如下一幅圖: ![示例 2](https://cdn.jsdelivr.net/gh/sunmyuan/cdn/210116_3.png) 如上圖所示,不同的消費者組消費同一個topic,這個topic有4個分割槽,分佈在兩個節點上。左邊的 消費組1有兩個消費者,每個消費者就要消費兩個分割槽才能把訊息完整的消費完,右邊的 消費組2有四個消費者,每個消費者消費一個分割槽即可。 **總結下kafka中分割槽與消費組的關係**: 消費組: 由一個或者多個消費者組成,同一個組中的消費者對於同一條訊息只消費一次。 **某一個主題下的分割槽數,對於消費該主題的同一個消費組下的消費者數量,應該小於等於該主題下的分割槽數**。 如:某一個主題有4個分割槽,那麼消費組中的消費者應該小於等於4,而且最好與分割槽數成整數倍 1 2 4 這樣。**同一個分割槽下的資料,在同一時刻,不能同一個消費組的不同消費者消費**。 總結:**分割槽數越多,同一時間可以有越多的消費者來進行消費,消費資料的速度就會越快,提高消費的效能**。 #### 6. partition replicas(分割槽副本) kafka 中的分割槽副本如下圖所示: ![kafka 分割槽副本](https://cdn.jsdelivr.net/gh/sunmyuan/cdn/210117_4.png) **副本數**(replication-factor):控制訊息儲存在幾個broker(伺服器)上,一般情況下副本數等於broker的個數。 一個broker服務下,不可以建立多個副本因子。**建立主題時,副本因子應該小於等於可用的broker數**。 副本因子操作以分割槽為單位的。每個分割槽都有各自的主副本和從副本; 主副本叫做leader,從副本叫做 follower(在有多個副本的情況下,kafka會為同一個分割槽下的所有分割槽,設定角色關係:一個leader和N個 follower),**處於同步狀態的副本叫做in-sync-replicas(ISR)**; follower通過拉的方式從leader同步資料。 **消費者和生產者都是從leader讀寫資料,不與follower互動**。 副本因子的作用:讓kafka讀取資料和寫入資料時的可靠性。 副本因子是包含本身,同一個副本因子不能放在同一個broker中。 如果某一個分割槽有三個副本因子,就算其中一個掛掉,那麼只會剩下的兩個中,選擇一個leader,但不會在其他的broker中,另啟動一個副本(因為在另一臺啟動的話,存在資料傳遞,只要在機器之間有資料傳遞,就會長時間佔用網路IO,kafka是一個高吞吐量的訊息系統,這個情況不允許發生)所以不會在另一個broker中啟動。 如果所有的副本都掛了,生產者如果生產資料到指定分割槽的話,將寫入不成功。 lsr表示:當前可用的副本。 #### 7. segment檔案 一個partition當中由多個segment檔案組成,每個segment檔案,包含兩部分,一個是 .log 檔案,另外一個是 .index 檔案,其中 .log 檔案包含了我們傳送的資料儲存,.index 檔案,記錄的是我們.log檔案的資料索引值,以便於我們加快資料的查詢速度。 **索引檔案與資料檔案的關係** 既然它們是一一對應成對出現,必然有關係。索引檔案中元資料指向對應資料檔案中message的物理偏移地址。 比如索引檔案中 3,497 代表:資料檔案中的第三個message,它的偏移地址為497。 再來看資料檔案中,Message 368772表示:在全域性partiton中是第368772個message。 > 注:segment index file 採取稀疏索引儲存方式,減少索引檔案大小,通過mmap(記憶體對映)可以直接記憶體操作,稀疏索引為資料檔案的每個對應message設定一個元資料指標,它比稠密索引節省了更多的儲存空間,但查詢起來需要消耗更多的時間。 .index 與 .log 對應關係如下: ![.index 與 .log](https://cdn.jsdelivr.net/gh/sunmyuan/cdn/210116_5.png) 上圖左半部分是索引檔案,裡面儲存的是一對一對的key-value,其中key是訊息在資料檔案(對應的log檔案)中的編號,比如“1,3,6,8……”, 分別表示在log檔案中的第1條訊息、第3條訊息、第6條訊息、第8條訊息…… 那麼為什麼在index檔案中這些編號不是連續的呢? 這是因為index檔案中並沒有為資料檔案中的每條訊息都建立索引,而是採用了稀疏儲存的方式,每隔一定位元組的資料建立一條索引。 這樣避免了索引檔案佔用過多的空間,從而可以將索引檔案保留在記憶體中。 但缺點是沒有建立索引的Message也不能一次定位到其在資料檔案的位置,從而需要做一次順序掃描,但是這次順序掃描的範圍就很小了。 value 代表的是在全域性partiton中的第幾個訊息。 以索引檔案中元資料 3,497 為例,其中3代表在右邊log資料檔案中從上到下第3個訊息, 497表示該訊息的物理偏移地址(位置)為497(也表示在全域性partiton表示第497個訊息-順序寫入特性)。 **log日誌目錄及組成** kafka在我們指定的log.dir目錄下,會建立一些資料夾;名字是 (主題名字-分割槽名) 所組成的資料夾。 在(主題名字-分割槽名)的目錄下,會有兩個檔案存在,如下所示: ```shell #索引檔案 00000000000000000000.index #日誌內容 00000000000000000000.log ``` 在目錄下的檔案,會根據log日誌的大小進行切分,.log檔案的大小為1G的時候,就會進行切分檔案;如下: ```shell -rw-r--r--. 1 root root 389k 1月 17 18:03 00000000000000000000.index -rw-r--r--. 1 root root 1.0G 1月 17 18:03 00000000000000000000.log -rw-r--r--. 1 root root 10M 1月 17 18:03 00000000000000077894.index -rw-r--r--. 1 root root 127M 1月 17 18:03 00000000000000077894.log ``` 在kafka的設計中,將offset值作為了檔名的一部分。 **segment檔案命名規則**:partion全域性的第一個segment從0開始,後續每個segment檔名為上一個全域性 partion的最大offset(偏移message數)。數值最大為64位long大小,20位數字字元長度,沒有數字就用 0 填充。 通過索引資訊可以快速定位到message。通過index元資料全部對映到記憶體,可以避免segment File的IO磁碟操作; 通過索引檔案稀疏儲存,可以大幅降低index檔案元資料佔用空間大小。 稀疏索引:為了資料建立索引,但範圍並不是為每一條建立,而是為某一個區間建立; 好處:就是可以減少索引值的數量。 不好的地方:找到索引區間之後,要得進行第二次處理。 #### 8. message的物理結構 生產者傳送到kafka的每條訊息,都被kafka包裝成了一個message message 的物理結構如下圖所示: ![.index 與 .log](https://cdn.jsdelivr.net/gh/sunmyuan/cdn/210116_6.png) 所以生產者傳送給kafka的訊息並不是直接儲存起來,而是經過kafka的包裝,每條訊息都是上圖這個結構,只有最後一個欄位才是真正生產者傳送的訊息資料。 ## kafka中的資料不丟失機制 ### 1. 生產者生產資料不丟失 #### 傳送訊息方式 生產者傳送給kafka資料,可以採用**同步方式**或**非同步方式** **同步方式**: 傳送一批資料給kafka後,等待kafka返回結果: 1. 生產者等待10s,如果broker沒有給出ack響應,就認為失敗。 2. 生產者重試3次,如果還沒有響應,就報錯. **非同步方式**: 傳送一批資料給kafka,只是提供一個回撥函式: 1. 先將資料儲存在生產者端的buffer中。buffer大小是2萬條 。 2. 滿足資料閾值或者數量閾值其中的一個條件就可以傳送資料。 3. 傳送一批資料的大小是500條。 > 注:如果broker遲遲不給ack,而buffer又滿了,開發者可以設定是否直接清空buffer中的資料。 #### ack機制(確認機制) 生產者資料傳送出去,需要服務端返回一個確認碼,即ack響應碼;ack的響應有三個狀態值0,1,-1 0:生產者只負責傳送資料,不關心資料是否丟失,丟失的資料,需要再次傳送 1:partition的leader收到資料,不管follow是否同步完資料,響應的狀態碼為1 -1:所有的從節點都收到資料,響應的狀態碼為-1 > 如果broker端一直不返回ack狀態,producer永遠不知道是否成功;producer可以設定一個超時時間10s,超過時間認為失敗。 ### 2. broker中資料不丟失 在broker中,保證資料不丟失主要是通過副本因子(冗餘),防止資料丟失。 ### 3. 消費者消費資料不丟失 在消費者消費資料的時候,只要每個消費者記錄好offset值即可,就能保證資料不丟失。也就是需要我們自己維護偏移量(offset),可儲存在 Redis 中。 ### 搜尋公眾號:五分鐘學大資料,深度鑽研大數