集群成員關(guān)系
Kafka 使用 Zookeeper 來維護集群成員的信息。每個 broker 都有一個唯一標識符,這個標識符可以在配置文件里指定,也可以自動生成。在 broker 啟動的時候,它通過創(chuàng)建臨時節(jié)點把自己的 ID 注冊到 Zookeeper。Kafka 組件訂閱 Zookeeper 的 /broker/ids 路徑。
控制器
控制器其實就是一個 broker。集群里第一個啟動的 broker 通過在 Zookeeper 里創(chuàng)建一個臨時節(jié)點 /controller 讓自己成為控制器。其他 broker 在控制器節(jié)點上創(chuàng)建 Zookeeper watch 對象,如果控制器被關(guān)閉或者與 Zookeeper 斷開連接,它們會嘗試讓自己成為新的控制器。每個新選出的控制器通過 Zookeeper 的條件遞增操作獲得一個全新的、數(shù)值更大的 controller epoch。
控制器遍歷分區(qū),并確定誰應該成為新首領(lǐng),隨后,新首領(lǐng)開始處理生產(chǎn)者和消費者的請求,而跟隨者開始從首領(lǐng)那里復制消息。
簡而言之,Kafka 使用 Zookeeper 的臨時節(jié)點來選舉控制器,并在節(jié)點加入集群或退出集群時通知控制器??刂破髫撠熢诠?jié)點加入或離開集群時進行分區(qū)首領(lǐng)選舉。控制器使用 epoch 來避免“腦裂”,“腦裂”是指兩個節(jié)點同時被認為自己是當前的控制器。
復制
復制功能是 Kafka 架構(gòu)的核心。Kafka 把自己描述成一個分布式的、可分區(qū)的、可復制的提交日志服務。
Kafka 使用主題來組織數(shù)據(jù),每個主題被分為若干個分區(qū),每個分區(qū)有多個副本。每個 broker 可以保存成百上千個屬于不同主題和分區(qū)的副本。
副本有兩種類型:
- 首領(lǐng)副本:每個分區(qū)都有一個首領(lǐng)副本。為了保證一致性,所有生產(chǎn)者請求和消費者請求都會經(jīng)過這個副本。
- 跟隨者副本:首領(lǐng)以外的副本都是跟隨者副本。跟隨者副本不處理來自客戶端的請求,它們唯一的任務就是從首領(lǐng)那么復制消息,保持與首領(lǐng)一致的狀態(tài)。
為了與首領(lǐng)保持同步,跟隨者向首領(lǐng)發(fā)送獲取數(shù)據(jù)的請求,這種請求與消費者為了讀取消息而發(fā)送的請求是一樣的。請求消息里包含了跟隨者想要獲取消息的偏移量,而這些偏移量總是有序的。通過查看每個跟隨者請求的最新偏移量,首領(lǐng)就會知道每個跟隨者復制的進度。如果跟隨者在10s內(nèi)沒有請求任何消息,或者雖然在請求消息,但是10s內(nèi)沒有請求最新的數(shù)據(jù),那么它就被認為是不同步的。
處理請求
broker 的大部分工作是處理客戶端、分區(qū)副本和控制器發(fā)送給分區(qū)首領(lǐng)的請求。Kafka 提供了一個二進制協(xié)議(基于 TCP),指定了請求消息的格式以及 broker 如何對請求作出響應。
broker 會在它所監(jiān)聽的每一個端口上運行一個 Acceptor 線程,這個線程會創(chuàng)建一個連接,并把它交給 Processor 線程去處理。Processor 線程負責從客戶端獲取請求消息,把它們放進請求隊列,然后從響應隊列獲取響應消息,把它們發(fā)送給客戶端。
生產(chǎn)請求和獲取請求都必須發(fā)送給分區(qū)的首領(lǐng)副本。Kafka 客戶端負責把生產(chǎn)請求和獲取請求發(fā)送到爭取的 broker 上。客戶端使用了另一種請求類型,也就是元數(shù)據(jù)請求。服務器端的響應消息里指明了這些主題所包含的分區(qū)、每個分區(qū)都有哪些副本,以及哪個副本是首領(lǐng)。一般情況下,客戶端會把這些信息緩存起來。
Kafka 使用零復制技術(shù)向客戶端發(fā)送消息 —— 也就是說,Kafka 直接把消息從文件(或者更確切的說是 Linux 文件系統(tǒng)緩存)里發(fā)送到網(wǎng)絡(luò)通道,而不需要經(jīng)過任何中間緩沖區(qū)。
并不是所有保存在分區(qū)首領(lǐng)上的數(shù)據(jù)都可以被客戶端讀取。大部分客戶端只能讀取已經(jīng)被寫入所有同步部分的消息。
物理存儲
Kafka 的基本存儲單元是分區(qū)。在配置 Kafka 的時候,管理員指定了一個用戶存儲分區(qū)的目錄清單。
分區(qū)分配
在創(chuàng)建主題時,Kafka 會決定如何在 broker 間分配分區(qū)。為分區(qū)和副本選好 broker 之后,會決定哪些分區(qū)使用哪些目錄。規(guī)則很簡單:計算每個目錄里的分區(qū)數(shù)量,新的分區(qū)總是被添加到數(shù)量最小的哪個目錄里。
Kafka 管理員為每個主題配置了數(shù)據(jù)保留期限,規(guī)定數(shù)據(jù)被刪除之前可以保留多長時間,或者清理數(shù)據(jù)之前可以保留數(shù)據(jù)量大小。
文件管理
因為一個大文件里查找和刪除消息是很費時的,所以把分區(qū)分成若干個片段。在 broker 往分區(qū)寫入數(shù)據(jù)時,如果達到片段上限,就關(guān)閉當前文件,并打開一個新文件。當前正在寫入數(shù)據(jù)的片段叫做活躍片段。
文件格式
Kafka 把消息和偏移量保存在文件里。
索引
消費者可以從 Kafka 的任意可用偏移量位置開始讀取消息。Kafka 為每個分區(qū)維護了一個索引。索引把偏移量映射到片段文件和偏移量在文件里的位置。
索引也被分成片段,所以再刪除消息時,也可以刪除相應的所以。如果索引出現(xiàn)損壞,Kafka 會通過重新讀取消息并錄制偏移量和位置來重新生成索引。
清理的工作原理
每個日志片段可以分為兩個部分:
- 干凈的部分:這些消息之前被清理過。
- 污濁的部分:這些消息是上一次清理之后寫入的。
如果在 Kafka 啟動時啟動了清理功能,每個 broker 會啟動一個清理管理器線程和多個清理線程,它們負責清理任務。為了清理分區(qū),清理線程會讀取分區(qū)的污濁部分,并在內(nèi)存里創(chuàng)建一個 map。map 里的每個元素包含了消息鍵的散列值和消息的偏移量。

獲取以上Java高級架構(gòu)最新視頻,歡迎
加入Java進階架構(gòu)交流群:142019080。直接點擊鏈接加群。https://jq.qq.com/?_wv=1027&k=5lXBNZ7