kafka如何實現高並發存儲-如何找到一條需要消費的數據(阿里)


阿里太注重原理了:阿里問kafka如何實現高並發存儲-如何找到一條需要消費的數據,kafka用了稀疏索引的方式,使用了二分查找法,其實很多索引都是二分查找法

 二分查找法的時間復雜度:O(logn) redis,kafka,B+樹的底層都采用了二分查找法 

參考:二分查找法 redis的索引底層的 跳表原理 實現 聊聊Mysql索引和redis跳表 ---redis的跳表原理 時間復雜度O(logn)(阿里) 

參考:二分查找法 mysql索引原理:一步步分析為什么B+樹適合作為索引的結構 以及索引原理 (阿里面試)

參考:二分查找法:各種排序算法的時間復雜度和空間復雜度(阿里)

這是答案:

在partition中如何通過offset查找message

例如讀取offset=368776的message,需要通過下面2個步驟查找。

第一步查找segment file

上述圖2為例,其中00000000000000000000.index表示最開始的文件,起始偏移量(offset)為0.第二個文件00000000000000368769.index的消息量起始偏移量為368770 = 368769 + 1.同樣,第三個文件00000000000000737337.index的起始偏移量為737338=737337 + 1,其他后續文件依次類推,以起始偏移量命名並排序這些文件,只要根據offset **二分查找**文件列表,就可以快速定位到具體文件。

當offset=368776時定位到00000000000000368769.index|log

第二步通過segment file查找message

通過第一步定位到segment file,當offset=368776時,依次定位到00000000000000368769.index的元數據物理位置和 00000000000000368769.log的物理偏移地址,然后再通過00000000000000368769.log順序查找直到 offset=368776為止

從上述圖3可知這樣做的優點,segment index file采取稀疏索引存儲方式,它減少索引文件大小,通過mmap可以直接內存操作,稀疏索引為數據文件的每個對應message設置一個元數據指針,它比稠密索引節省了更多的存儲空間,但查找起來需要消耗更多的時間。

具體參考:  

Kafka 社區非常活躍,從 0.9 版本開始,Kafka 的標語已經從“一個高吞吐量,分布式的消息系統”改為"一個分布式流平台"。

Kafka 和傳統的消息系統不同在於:

  • Kafka是一個分布式系統,易於向外擴展。
  • 它同時為發布和訂閱提供高吞吐量。
  • 它支持多訂閱者,當失敗時能自動平衡消費者。
  • 消息的持久化。

Kafka 和其他消息隊列的對比:

 Kafka 架構原理

對於 Kafka 的架構原理,我們先提出如下幾個問題:

  • Kafka 的 topic 和分區內部是如何存儲的,有什么特點?
  • 與傳統的消息系統相比,Kafka 的消費模型有什么優點?
  • Kafka 如何實現分布式的數據存儲與數據讀取?

Kafka 架構圖

Kafka 名詞解釋

在一套 Kafka 架構中有多個 Producer,多個 Broker,多個 Consumer,每個 Producer 可以對應多個 Topic,每個 Consumer 只能對應一個 Consumer Group。

整個 Kafka 架構對應一個 ZK 集群,通過 ZK 管理集群配置,選舉 Leader,以及在 Consumer Group 發生變化時進行 Rebalance。

Topic 和 Partition

在 Kafka 中的每一條消息都有一個 Topic。一般來說在我們應用中產生不同類型的數據,都可以設置不同的主題。

一個主題一般會有多個消息的訂閱者,當生產者發布消息到某個主題時,訂閱了這個主題的消費者都可以接收到生產者寫入的新消息。

Kafka 為每個主題維護了分布式的分區(Partition)日志文件,每個 Partition 在 Kafka 存儲層面是 Append Log。

任何發布到此 Partition 的消息都會被追加到 Log 文件的尾部,在分區中的每條消息都會按照時間順序分配到一個單調遞增的順序編號,也就是我們的 Offset。Offset 是一個 Long 型的數字。

我們通過這個 Offset 可以確定一條在該 Partition 下的唯一消息。在 Partition 下面是保證了有序性,但是在 Topic 下面沒有保證有序性。

在上圖中我們的生產者會決定發送到哪個 Partition:

如果沒有 Key 值則進行輪詢發送。

如果有 Key 值,對 Key 值進行 Hash,然后對分區數量取余,保證了同一個 Key 值的會被路由到同一個分區;如果想隊列的強順序一致性,可以讓所有的消息都設置為同一個 Key。

消費模型

消息由生產者發送到 Kafka 集群后,會被消費者消費。一般來說我們的消費模型有兩種:

  • 推送模型(Push)
  • 拉取模型(Pull)

基於推送模型的消息系統,由消息代理記錄消費狀態。消息代理將消息推送到消費者后,標記這條消息為已經被消費,但是這種方式無法很好地保證消費的處理語義。

比如當我們已經把消息發送給消費者之后,由於消費進程掛掉或者由於網絡原因沒有收到這條消息,如果我們在消費代理將其標記為已消費,這個消息就***丟失了。

如果我們利用生產者收到消息后回復這種方法,消息代理需要記錄消費狀態,這種不可取。

如果采用 Push,消息消費的速率就完全由消費代理控制,一旦消費者發生阻塞,就會出現問題。

Kafka 采取拉取模型(Poll),由自己控制消費速度,以及消費的進度,消費者可以按照任意的偏移量進行消費。

比如消費者可以消費已經消費過的消息進行重新處理,或者消費最近的消息等等。

網絡模型

Kafka Client:單線程 Selector

單線程模式適用於並發鏈接數小,邏輯簡單,數據量小的情況。在 Kafka 中,Consumer 和 Producer 都是使用的上面的單線程模式。

這種模式不適合 Kafka 的服務端,在服務端中請求處理過程比較復雜,會造成線程阻塞,一旦出現后續請求就會無法處理,會造成大量請求超時,引起雪崩。而在服務器中應該充分利用多線程來處理執行邏輯。

Kafka Server:多線程 Selector

在 Kafka 服務端采用的是多線程的 Selector 模型,Acceptor 運行在一個單獨的線程中,對於讀取操作的線程池中的線程都會在 Selector 注冊 Read 事件,負責服務端讀取請求的邏輯。

成功讀取后,將請求放入 Message Queue共享隊列中。然后在寫線程池中,取出這個請求,對其進行邏輯處理。

這樣,即使某個請求線程阻塞了,還有后續的線程從消息隊列中獲取請求並進行處理,在寫線程中處理完邏輯處理,由於注冊了 OP_WIRTE 事件,所以還需要對其發送響應。

高可靠分布式存儲模型

在 Kafka 中保證高可靠模型依靠的是副本機制,有了副本機制之后,就算機器宕機也不會發生數據丟失。

高性能的日志存儲 kafka采用了稀疏索引的方式 

Kafka 一個 Topic 下面的所有消息都是以 Partition 的方式分布式的存儲在多個節點上。

同時在 Kafka 的機器上,每個 Partition 其實都會對應一個日志目錄,在目錄下面會對應多個日志分段(LogSegment)。

LogSegment 文件由兩部分組成,分別為“.index”文件和“.log”文件,分別表示為 Segment 索引文件和數據文件。 

先通過index文件,利用二分查找法,找到相應的稀疏索引,然后跟進index上的偏移量,找到log文件的位置,然后在log順序遍歷上面找到相應的文件;

 

2.4 在partition中如何通過offset查找message

例如讀取offset=368776的message,需要通過下面2個步驟查找。

第一步查找segment file

上述圖2為例,其中00000000000000000000.index表示最開始的文件,起始偏移量(offset)為0.第二個文件00000000000000368769.index的消息量起始偏移量為368770 = 368769 + 1.同樣,第三個文件00000000000000737337.index的起始偏移量為737338=737337 + 1,其他后續文件依次類推,以起始偏移量命名並排序這些文件,只要根據offset **二分查找**文件列表,就可以快速定位到具體文件。

當offset=368776時定位到00000000000000368769.index|log

第二步通過segment file查找message

通過第一步定位到segment file,當offset=368776時,依次定位到00000000000000368769.index的元數據物理位置和 00000000000000368769.log的物理偏移地址,然后再通過00000000000000368769.log順序查找直到 offset=368776為止。

從上述圖3可知這樣做的優點,segment index file采取稀疏索引存儲方式,它減少索引文件大小,通過mmap可以直接內存操作,稀疏索引為數據文件的每個對應message設置一個元數據指針,它比稠密索引節省了更多的存儲空間,但查找起來需要消耗更多的時間。

 

2.Kafka文件存儲機制

Kafka部分名詞解釋如下:

Broker:消息中間件處理結點,一個Kafka節點就是一個broker,多個broker可以組成一個Kafka集群。

Topic:一類消息,例如page view日志、click日志等都可以以topic的形式存在,Kafka集群能夠同時負責多個topic的分發。

Partition:topic物理上的分組,一個topic可以分為多個partition,每個partition是一個有序的隊列。

Segment:partition物理上由多個segment組成,下面2.2和2.3有詳細說明。

offset:每個partition都由一系列有序的、不可變的消息組成,這些消息被連續的追加到partition中。partition中的每個消息都有一個連續的序列號叫做offset,用於partition唯一標識一條消息. 

分析過程分為以下4個步驟:

topic中partition存儲分布

partiton中文件存儲方式

partiton中segment文件存儲結構

在partition中如何通過offset查找message

通過上述4過程詳細分析,我們就可以清楚認識到kafka文件存儲機制的奧秘。

2.1 topic中partition存儲分布

假設實驗環境中Kafka集群只有一個broker,xxx/message-folder為數據文件存儲根目錄,在Kafka broker中server.properties文件配置(參數log.dirs=xxx/message-folder),例如創建2個topic名稱分別為report_push、launch_info, partitions數量都為partitions=4(將一個topic分為4個部分存儲

存儲路徑和目錄規則為:

xxx/message-folder

|--report_push-0

|--report_push-1

|--report_push-2

|--report_push-3

|--launch_info-0

|--launch_info-1

|--launch_info-2

|--launch_info-3

在Kafka文件存儲中,同一個topic下有多個不同partition,每個partition為一個目錄,partiton命名規則為topic名稱+有序序號,第一個partiton序號從0開始,序號最大值為partitions數量減1。

如果是多broker分布情況,請參考文末kafka集群partition分布原理分析

2.2 partiton中文件存儲方式

下面示意圖形象說明了partition中文件存儲方式:

 圖1

每個partion(目錄)相當於一個巨型文件被平均分配到多個大小相等segment(段)數據文件中。但每個段segment file消息數量不一定相等,這種特性方便old segment file快速被刪除。

每個partiton只需要支持順序讀寫就行了,segment文件生命周期由服務端配置參數決定。

這樣做的好處就是能快速刪除無用文件,有效提高磁盤利用率。

2.3 partiton中segment文件存儲結構

讀者從2.2節了解到Kafka文件系統partition存儲方式,本節深入分析partion中segment file組成和物理結構。

segment file組成:由2大部分組成,分別為index file和data file,此2個文件一一對應,成對出現,后綴".index"和“.log”分別表示為segment索引文件、數據文件.

segment文件命名規則:partion全局的第一個segment從0開始,后續每個segment文件名為上一個segment文件最后一條消息的offset值。數值最大為64位long大小,19位數字字符長度,沒有數字用0填充。

下面文件列表是筆者在Kafka broker上做的一個實驗,創建一個topicXXX包含1 partition,設置每個segment大小為500MB,並啟動producer向Kafka broker寫入大量數據,如下圖2所示segment文件列表形象說明了上述2個規則:

以上述圖2中一對segment file文件為例,說明segment中index<—->data file對應關系物理結構如下:

上述圖3中索引文件存儲大量元數據,數據文件存儲大量消息,索引文件中元數據指向對應數據文件中message的物理偏移地址。

其中以索引文件中元數據3,497為例,依次在數據文件中表示第3個message(在全局partiton表示第368772個message)、以及該消息的物理偏移地址為497。

從上述圖3了解到segment data file由許多message組成,下面詳細說明message物理結構如下:

圖4 

參數說明:

關鍵字 解釋說明
8 byte offset 在parition(分區)內的每條消息都有一個有序的id號,這個id號被稱為偏移(offset),它可以唯一確定每條消息在parition(分區)內的位置。即offset表示partiion的第多少message
4 byte message size message大小
4 byte CRC32 用crc32校驗message
1 byte “magic" 表示本次發布Kafka服務程序協議版本號
1 byte “attributes" 表示為獨立版本、或標識壓縮類型、或編碼類型。
4 byte key length 表示key的長度,當key為-1時,K byte key字段不填
K byte key 可選
value bytes payload 表示實際消息數據。

 

 

3 Kafka文件存儲機制?實際運行效果

實驗環境:

Kafka集群:由2台虛擬機組成

cpu:4核

物理內存:8GB

網卡:千兆網卡

jvm heap: 4GB

詳細Kafka服務端配置及其優化請參考:kafka server.properties配置詳解

圖5

從上述圖5可以看出,Kafka運行時很少有大量讀磁盤的操作,主要是定期批量寫磁盤操作,因此操作磁盤很高效。這跟Kafka文件存儲中讀寫message的設計是息息相關的。Kafka中讀寫message有如下特點:

寫message

消息從java堆轉入page cache(即物理內存)。

由異步線程刷盤,消息從page cache刷入磁盤。

讀message

消息直接從page cache轉入socket發送出去。

當從page cache沒有找到相應數據時,此時會產生磁盤IO,從磁

盤Load消息到page cache,然后直接從socket發出去

4.總結

Kafka高效文件存儲設計特點

Kafka把topic中一個parition大文件分成多個小文件段,通過多個小文件段,就容易定期清除或刪除已經消費完文件,減少磁盤占用。

通過索引信息可以快速定位message和確定response的最大大小。

通過index元數據全部映射到memory,可以避免segment file的IO磁盤操作。

通過索引文件稀疏存儲,可以大幅降低index文件元數據占用空間大小。

 

topic------->多個partiton----------->1個partion多個segment----------->1個segment多個index和log

在只有一個broker的時候,多個partion位於這個broker,有多個broker的時候是按照一定的算法分布在多個broker上。

5.kafka中的partition和offset

5.1Log機制

說到分區,就要說kafka對消息的存儲.在官方文檔中.

 

分區讀寫日志圖

 

首先,kafka是通過log(日志)來記錄消息發布的.每當產生一個消息,kafka會記錄到本地的log文件中,這個log和我們平時的log有一定的區別.這里可以參考一下The Log,不多解釋.

這個log文件默認的位置在config/server.properties中指定的.默認的位置是log.dirs=/tmp/kafka-logs,linux不用說,windows的話就在你對應磁盤的根目錄下.我這里是D盤.

 

分區partition

kafka是為分布式環境設計的,因此如果日志文件,其實也可以理解成消息數據庫,放在同一個地方,那么必然會帶來可用性的下降,一掛全掛,如果全量拷貝到所有的機器上,那么數據又存在過多的冗余,而且由於每台機器的磁盤大小是有限的,所以即使有再多的機器,可處理的消息還是被磁盤所限制,無法超越當前磁盤大小.因此有了partition的概念.

kafka對消息進行一定的計算,通過hash來進行分區.這樣,就把一份log文件分成了多份.如上面的分區讀寫日志圖,分成多份以后,在單台broker上,比如快速上手中,如果新建topic的時候,我們選擇了--replication-factor 1 --partitions 2,那么在log目錄里,我們會看到

test-0目錄和test-1目錄.就是兩個分區了.

你可能會想,這特么沒啥區別呀.注意,當有了多個broker之后,這個意義就存在了.這里上一張圖,原文在參考鏈接里有

 

5.2 kafka分布式分區存儲

 

這是一個topic包含4個Partition2 Replication(拷貝),也就是說全部的消息被放在了4個分區存儲,為了高可用,將4個分區做了2份冗余,然后根據分配算法.將總共8份數據,分配到broker集群上.

結果就是每個broker上存儲的數據比全量數據要少,但每份數據都有冗余,這樣,一旦一台機器宕機,並不影響使用.比如圖中的Broker1,宕機了.那么剩下的三台broker依然保留了全量的分區數據.所以還能使用,如果再宕機一台,那么數據不完整了.當然你可以設置更多的冗余,比如設置了冗余是4,那么每台機器就有了0123完整的數據,宕機幾台都行.需要在存儲占用和高可用之間做衡量.

至於宕機后,zookeeper會選出新的partition leader.來提供服務.這個等下篇文章

偏移offset

上一段說了分區,分區就是一個有序的,不可變的消息隊列.新來的commit log持續往后面加數據.這些消息被分配了一個下標(或者偏移),就是offset,用來定位這一條消息.

消費者消費到了哪條消息,是保持在消費者這一端的.消息者也可以控制,消費者可以在本地保存最后消息的offset,並間歇性的向zookeeper注冊offset.也可以重置offset

如何通過offset算出分區

其實partition存儲的時候,又分成了多個segment(段),然后通過一個index,索引,來標識第幾段.這里先可以去看一下本地log目錄的分區文件夾.

在我這里,test-0,這個分區里面,會有一個index文件和一個log文件,

 

index和log

 

對於某個指定的分區,假設每5個消息,作為一個段大小,當產生了10條消息的情況想,目前有會得到(只是解釋)

0.index (表示這里index是對0-4做的索引)

5.index (表示這里index是對5-9做的索引)

10.index (表示這里index是對10-15做的索引,目前還沒滿)

0.log

5.log

10.log

,當消費者需要讀取offset=8的時候,首先kafka對index文件列表進行二分查找,可以算出.應該是在5.index對應的log文件中,然后對對應的5.log文件,進行順序查找,5->6->7->8,直到順序找到8就好了. 

以上是Kafka文件存儲機制及partition和offset的全部內容,在雲棲社區的博客、問答、雲棲號、人物、課程等欄目也有Kafka文件存儲機制及partition和offset的相關內容,歡迎繼續使用右上角搜索按鈕進行搜索存儲 , 文件 , 數據 , 索引 , 磁盤 物理 kafka offset保存機制、kafka offset 存儲、kafka partition、kafka partition 設置、kafka partition 數量,以便於您獲取更多的相關知識。

 

 

 

 

副本機制

Kafka 的副本機制是多個服務端節點對其他節點的主題分區的日志進行復制。

當集群中的某個節點出現故障,訪問故障節點的請求會被轉移到其他正常節點(這一過程通常叫 Reblance)。

Kafka 每個主題的每個分區都有一個主副本以及 0 個或者多個副本,副本保持和主副本的數據同步,當主副本出故障時就會被替代。

在 Kafka 中並不是所有的副本都能被拿來替代主副本,所以在 Kafka 的 Leader 節點中維護着一個 ISR(In Sync Replicas)集合。

翻譯過來也叫正在同步中集合,在這個集合中的需要滿足兩個條件:

  • 節點必須和 ZK 保持連接。
  • 在同步的過程中這個副本不能落后主副本太多。

另外還有個 AR(Assigned Replicas)用來標識副本的全集,OSR 用來表示由於落后被剔除的副本集合。

所以公式如下:ISR = Leader + 沒有落后太多的副本;AR = OSR+ ISR。

這里先要說下兩個名詞:HW(高水位)是 Consumer 能夠看到的此 Partition 的位置,LEO 是每個 Partition 的 Log ***一條 Message 的位置。

HW 能保證 Leader 所在的 Broker 失效,該消息仍然可以從新選舉的 Leader 中獲取,不會造成消息丟失。

當 Producer 向 Leader 發送數據時,可以通過 request.required.acks 參數來設置數據可靠性的級別:

  • 1(默認):這意味着 Producer 在 ISR 中的 Leader 已成功收到的數據並得到確認后發送下一條 Message。如果 Leader 宕機了,則會丟失數據。
  • 0:這意味着 Producer 無需等待來自 Broker 的確認而繼續發送下一批消息。這種情況下數據傳輸效率***,但是數據可靠性卻是***的。
  • -1:Producer 需要等待 ISR 中的所有 Follower 都確認接收到數據后才算一次發送完成,可靠性***。

但是這樣也不能保證數據不丟失,比如當 ISR 中只有 Leader 時(其他節點都和 ZK 斷開連接,或者都沒追上),這樣就變成了 acks = 1 的情況。

高可用模型及冪等

在分布式系統中一般有三種處理語義:

at-least-once

至少一次,有可能會有多次。如果 Producer 收到來自 Ack 的確認,則表示該消息已經寫入到 Kafka 了,此時剛好是一次,也就是我們后面的 Exactly-once。

但是如果 Producer 超時或收到錯誤,並且 request.required.acks 配置的不是 -1,則會重試發送消息,客戶端會認為該消息未寫入 Kafka。

如果 Broker 在發送 Ack 之前失敗,但在消息成功寫入 Kafka 之后,這一次重試將會導致我們的消息會被寫入兩次。

所以消息就不止一次地傳遞給最終 Consumer,如果 Consumer 處理邏輯沒有保證冪等的話就會得到不正確的結果。

在這種語義中會出現亂序,也就是當***次 Ack 失敗准備重試的時候,但是第二消息已經發送過去了,這個時候會出現單分區中亂序的現象。

我們需要設置 Prouducer 的參數 max.in.flight.requests.per.connection,flight.requests 是 Producer 端用來保存發送請求且沒有響應的隊列,保證 Produce r端未響應的請求個數為 1。

at-most-once

如果在 Ack 超時或返回錯誤時 Producer 不重試,也就是我們講 request.required.acks = -1,則該消息可能最終沒有寫入 Kafka,所以 Consumer 不會接收消息。

exactly-once

剛好一次,即使 Producer 重試發送消息,消息也會保證最多一次地傳遞給 Consumer。該語義是最理想的,也是最難實現的。

在 0.10 之前並不能保證 exactly-once,需要使用 Consumer 自帶的冪等性保證。0.11.0 使用事務保證了。

如何實現 exactly-once

要實現 exactly-once 在 Kafka 0.11.0 中有兩個官方策略:

單 Producer 單 Topic

每個 Producer 在初始化的時候都會被分配一個唯一的 PID,對於每個唯一的 PID,Producer 向指定的 Topic 中某個特定的 Partition 發送的消息都會攜帶一個從 0 單調遞增的 Sequence Number。

在我們的 Broker 端也會維護一個維度為,每次提交一次消息的時候都會對齊進行校驗:

  • 如果消息序號比 Broker 維護的序號大一以上,說明中間有數據尚未寫入,也即亂序,此時 Broker 拒絕該消息,Producer 拋出 InvalidSequenceNumber。
  • 如果消息序號小於等於 Broker 維護的序號,說明該消息已被保存,即為重復消息,Broker 直接丟棄該消息,Producer 拋出 DuplicateSequenceNumber。
  • 如果消息序號剛好大一,就證明是合法的。

上面所說的解決了兩個問題:

  • 當 Prouducer 發送了一條消息之后失敗,Broker 並沒有保存,但是第二條消息卻發送成功,造成了數據的亂序。
  • 當 Producer 發送了一條消息之后,Broker 保存成功,Ack 回傳失敗,Producer 再次投遞重復的消息。

上面所說的都是在同一個 PID 下面,意味着必須保證在單個 Producer 中的同一個 Seesion 內,如果 Producer 掛了,被分配了新的 PID,這樣就無法保證了,所以 Kafka 中又有事務機制去保證。

事務

在 Kafka 中事務的作用是:

  • 實現 exactly-once 語義。
  • 保證操作的原子性,要么全部成功,要么全部失敗。
  • 有狀態的操作的恢復。

事務可以保證就算跨多個,在本次事務中的對消費隊列的操作都當成原子性,要么全部成功,要么全部失敗。

並且,有狀態的應用也可以保證重啟后從斷點處繼續處理,也即事務恢復。

在 Kafka 的事務中,應用程序必須提供一個唯一的事務 ID,即 Transaction ID,並且宕機重啟之后,也不會發生改變。

Transactin ID 與 PID 可能一一對應,區別在於 Transaction ID 由用戶提供,而 PID 是內部的實現對用戶透明。

為了 Producer 重啟之后,舊的 Producer 具有相同的 Transaction ID 失效,每次 Producer 通過 Transaction ID 拿到 PID 的同時,還會獲取一個單調遞增的 Epoch。

由於舊的 Producer 的 Epoch 比新 Producer 的 Epoch 小,Kafka 可以很容易識別出該 Producer 是老的,Producer 並拒絕其請求。

為了實現這一點,Kafka 0.11.0.0 引入了一個服務器端的模塊,名為 Transaction Coordinator,用於管理 Producer 發送的消息的事務性。

該 Transaction Coordinator 維護 Transaction Log,該 Log 存於一個內部的 Topic 內。

由於 Topic 數據具有持久性,因此事務的狀態也具有持久性。Producer 並不直接讀寫 Transaction Log,它與 Transaction Coordinator 通信,然后由 Transaction Coordinator 將該事務的狀態插入相應的 Transaction Log。

Transaction Log 的設計與 Offset Log 用於保存 Consumer 的 Offset 類似。

***

關於消息隊列或者 Kafka 的一些常見的面試題,通過上面的文章可以提煉出以下幾個比較經典的問題,大部分問題都可以從上面總結后找到答案:

  • 為什么使用消息隊列?消息隊列的作用是什么?
  • Kafka 的 Topic 和分區內部是如何存儲的,有什么特點?
  • 與傳統的消息系統相比,Kafka 的消費模型有什么優點?
  • Kafka 如何實現分布式的數據存儲與數據讀取?
  • Kafka 為什么比 RocketMQ 支持的單機 Partition 要少?
  • 為什么需要分區,也就是說主題只有一個分區,難道不行嗎?
  • 日志為什么需要分段?
  • Kafka 是依靠什么機制保持高可靠,高可用?
  • 消息隊列如何保證消息冪等?
  • 讓你自己設計個消息隊列,你會怎么設計,會考慮哪些方面?

 

kafka工作原理

目錄

Kafka系統的角色

  • Broker :一台kafka服務器就是一個broker。一個集群由多個broker組成。一個broker可以容納多個topic
  • topic: 可以理解為一個MQ消息隊列的名字
  • Partition:
    • 為了實現擴展性,一個非常大的topic可以分布到多個 broker(即服務器)上,一個topic可以分為多個partition,每個partition是一個有序的隊列。
    • partition中的每條消息都會被分配一個有序的id(offset)。kafka只保證按一個partition中的順序將消息發給consumer,不保證一個topic的整體 (多個partition間)的順序。
    • 也就是說,一個topic在集群中可以有多個partition,那么分區的策略是什么?(消息發送到哪個分區上,有兩種基本的策略,一是采用Key Hash算法,一是采用Round Robin算法)

  • Offset:kafka的存儲文件都是按照offset.kafka來命名,用offset做名字的好處是方便查找。例如你想找位於2049的位置,只要找到2048.kafka的文件即可。當然the first offset就是00000000000.kafka
  • Producer :消息生產者,就是向kafka broker發消息的客戶端。
  • Consumer :消息消費者,向kafka broker取消息的客戶端

  • Consumer Group (CG):
    • 消息系統有兩類,一是廣播,二是訂閱發布。廣播是把消息發送給所有的消費者;發布訂閱是把消息只發送給訂閱者。Kafka通過Consumer Group組合實現了這兩種機制: 實現一個topic消息廣播(發給所有的consumer)和單播(發給任意一個consumer)。一個topic可以有多個CG。
    • topic的消息會復制(不是真的復制,是概念上的)到所有的CG,但每個CG只會把消息發給該CG中的一個 consumer(這是實現一個Topic多Consumer的關鍵點:為一個Topic定義一個CG,CG下定義多個Consumer)。如果需要實現廣播,只要每個consumer有一個獨立的CG就可以了。要實現單播只要所有的consumer在同一個CG。用CG還可以將consumer進行自由的分組而不需要多次發送消息到不同的topic。
    • 典型的應用場景是,多個Consumer來讀取一個Topic(理想情況下是一個Consumer讀取Topic的一個Partition),那么可以讓這些Consumer屬於同一個Consumer Group即可實現消息的多Consumer並行處理,原理是Kafka將一個消息發布出去后,ConsumerGroup中的Consumers可以通過Round Robin的方式進行消費(Consumers之間的負載均衡使用Zookeeper來實現)

A two server Kafka cluster hosting four partitions (P0-P3) with two consumer groups. Consumer group A has two consumer instances and group B has four.

Topic、Partition和Replica的關系

如上圖,一個Topic有四個Partition,每個Partition兩個replication。

Zookeeper在Kakfa中扮演的角色Kafka將元數據信息保存在Zookeeper中,但是發送給Topic本身的數據是不會發到Zk上的,否則Zk就瘋了。

  • kafka使用zookeeper來實現動態的集群擴展,不需要更改客戶端(producer和consumer)的配置。broker會在zookeeper注冊並保持相關的元數據(topic,partition信息等)更新。
  • 而客戶端會在zookeeper上注冊相關的watcher。一旦zookeeper發生變化,客戶端能及時感知並作出相應調整。這樣就保證了添加或去除broker時,各broker間仍能自動實現負載均衡。這里的客戶端指的是Kafka的消息生產端(Producer)和消息消費端(Consumer)
  • Broker端使用zookeeper來注冊broker信息,以及監測partition leader存活性。
  • Consumer端使用zookeeper用來注冊consumer信息,其中包括consumer消費的partition列表等,同時也用來發現broker列表,並和partition leader建立socket連接,並獲取消息。
  • Zookeeper和Producer沒有建立關系,只和Brokers、Consumers建立關系以實現負載均衡,即同一個Consumer Group中的Consumers可以實現負載均衡。

問題

  • Topic有多個Partition,那么消息分配到某個Partition的依據是什么?
    • Key Hash或者Round Robin
  • 如何查看一個Topic有多少個Partition?
    • 使用kakfa-topic.sh --list topic topicName --zookeeper zookeeper.servers.list

zookeeper存儲結果

    [zk: localhost:2181(CONNECTED) 0] ls /
    [admin, consumers, config, brokers]

 

參考:Kafka的架構原理,你真的理解嗎?

參考:kafka工作原理

參考:Kafka文件存儲機制及partition和offset


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM