kafka架構原理

1 Kafka介紹

Kafka是最初由Linkedin公司開發,是一個分佈式、支持分區的(partition)、多副本的(replica),基於zookeeper協調的分佈式消息系統,它的最大的特性就是可以實時的處理大量數據以滿足各種需求場景:比如基於hadoop的批處理系統、低延遲的實時系統、storm/Spark流式處理引擎,web/nginx日誌、訪問日誌,消息服務等等,用scala語言編寫,Linkedin於2010年貢獻給了Apache基金會併成為頂級開源 項目。

2 為什麼使用Kafka

2.1 解耦

允許你獨立的擴展或修改兩邊的處理過程,只要確保它們遵守同樣的接口約束。

2.2 冗餘

消息隊列把數據進行持久化直到它們已經被完全處理,通過這一方式規避了數據丟失風險。許多消息隊列所採用的"插入-獲取-刪除"範式中,在把一個消息從隊列中刪除之前,需要你的處理系統明確的指出該消息已經被處理完畢,從而確保你的數據被安全的保存直到你使用完畢。

2.3 擴展性

因為消息隊列解耦了你的處理過程,所以增大消息入隊和處理的頻率是很容易的,只要另外增加處理過程即可。

2.4 靈活性 & 峰值處理能力

在訪問量劇增的情況下,應用仍然需要繼續發揮作用,但是這樣的突發流量並不常見。如果為以能處理這類峰值訪問為標準來投入資源隨時待命無疑是巨大的浪費。使用消息隊列能夠使關鍵組件頂住突發的訪問壓力,而不會因為突發的超負荷的請求而完全崩潰。

2.5 可恢復性

系統的一部分組件失效時,不會影響到整個系統。消息隊列降低了進程間的耦合度,所以即使一個處理消息的進程掛掉,加入隊列中的消息仍然可以在系統恢復後被處理。

2.6 順序保證

在大多使用場景下,數據處理的順序都很重要。大部分消息隊列本來就是排序的,並且能保證數據會按照特定的順序來處理。(Kafka 保證一個 Partition 內的消息的有序性)

2.7 緩衝

有助於控制和優化數據流經過系統的速度,解決生產消息和消費消息的處理速度不一致的情況。

2.8 異步通信

很多時候,用戶不想也不需要立即處理消息。消息隊列提供了異步處理機制,允許用戶把一個消息放入隊列,但並不立即處理它。想向隊列中放入多少消息就放多少,然後在需要的時候再去處理它們。

3 Kafka架構

kafka架構原理

image

3.1 Broker

kafka 集群中包含的服務器。一個單獨的Kafka server就是一個Broker。Broker的主要工作就是接受生產者發過來的消息,分配offset,之後保存到磁盤中,同時,接收消費者、其他Borker的請求,根據請求類型進行相應處理並返回響應。多個Broker可以做成一個Cluster對外提供服務,每個Cluster當中會選舉出一個Broker來擔任Controller,Controller是Kafka集群的指揮中心,而其他Broker則聽從Controller指揮實現相應的功能。Controller負責管理分區的狀態、管理每個分區的副本的狀態、監聽Zookeeper中數據的變化等工作。Controller也是一主多從的實現,所有的Broker都會監聽Controller Leader的狀態,當Controller Leader出現故障時則重新選舉新的Controller Leader。

3.2 Producer

producer 發送消息到 broker 時,會根據分區算法選擇將其存儲到哪一個 partition。其路由機制為:

1 指定了 patition,則直接使用。

2 未指定 patition 但指定 key,通過對 key 的 value 進行hash 選出一個 patition。

3 patition 和 key 都未指定,使用輪詢選出一個 patition。

kafka架構原理

image

流程說明:

1 producer 先從 zookeeper 的 "/brokers/.../state" 節點找到該 partition 的 leader。

2 producer 將消息發送給該 leader。

3 leader 將消息寫入本地 log。

4 followers 從 leader pull 消息,寫入本地 log 後 leader 發送 ACK。

5 leader 收到所有 ISR 中的 replica 的 ACK 後,增加 HW(high watermark,最後 commit 的 offset) 並向 producer 發送 ACK。

下面來介紹消息傳遞的保證(Delivery guarantee semantic)的相關內容,傳遞保證語義有以下三個級別。

1 At most once:消息可能會丟,但絕不會重複傳遞。

2 At least once:消息絕不會丟,但可能會重複傳遞。

3 Exactly once:每條消息都只被傳遞一次。

當 producer 向 broker 發送消息時,一旦這條消息被 commit,由於 replication 的存在,它就不會丟。但是如果 producer 發送數據給 broker 後,遇到網絡問題而造成通信中斷,那 Producer 就無法判斷該條消息是否已經 commit。雖然 Kafka 無法確定網絡故障期間發生了什麼。為了實現Exactly once語義,這裡提供兩種可選方案:

1 每個分區只有一個生產者寫入消息,當出現異常或超時的情況時,生產者就要查詢此分區的最後一個消息,用來決定後續操作的消息重傳還是繼續發送。

2 為每個消息添加一個全局唯一主鍵,生產者不做其他特殊處理,按照之前分析的方式進行重傳,由消費者進行去重。

3.3 Topic&Log

每條發佈到 kafka 集群的消息屬於的類別,即 kafka 是面向 Topic的。每個Topic可以劃分成多個分區,同一個Topic下的不同分區包含的消息是不同的。每個消息在被添加到分區時,都會被分配到一個offset,它是消息在此分區中的唯一編號,Kafka通過offset保證消息的分區內的順序,offset的順序性不誇分區,即Kafka只保證在同一個分區內的消息是有序的。

kafka架構原理

image

同一Topic的不同分區會分配在不同的Broker上。分區是Kafka水平擴展性的基礎,我們可以通過增加服務器並在其上分配Partition的方式來增加Kafka的並行處理能力。

分區在邏輯上對應著一個Log,當生產者將消息寫入分區時,實際上是寫入到了分區對應的Log中。Log是一個邏輯概念,可以對應到磁盤上的一個文件夾。Log有多個Segment組成,每個Segment對應一個日誌文件和索引文件。在面對海量數據時,為避免Segment出現超大文件,每個日誌文件的大小是有限制的,當超出限制後會創建新的Segment繼續對外提供服務。這裡要注意,因為Kafka採用順序IO,所以只向最新的Segment追加數據。為了權衡文件大小、索引速度、佔用內存大小等多方面因素,索引文件採用稀疏索引的方式,大小並不會很大,在運行時會將其內容映射到內存,提高索引速度。

3.4 Partition

每一個Topic都可以劃分成多個Partition(每一個Topic都至少有一個Partition),不同的Partition會分配在不同的Broker上以對Kafka進行水平擴展從而增加Kafka的並行處理能力。同一個Topic下的不同Partition包含的消息是不同的。每一個消息在被添加到Partition的時候,都會被分配一個offset,他是消息在此分區中的唯一編號,此外,Kafka通過offset保證消息在Partition中的順序,offset的順序性不跨Partition,也就是說在Kafka的同一個Partition中的消息是有序的,不同Partition的消息可能不是有序的。

3.5 Consumer

消費者(Consumer)的主要工作是從Topic中拉取消息,並對消息進行消費。某個消息消費到Partition的哪個位置(offset)的相關信息,是Consumer自己維護的。

kafka架構原理

image

這樣設計非常巧妙。避免了Kafka Server端維護消費者消費位置的開銷,尤其是在消費數量較多的情況下。另一方面,如果是由Kafka Server端管理每個Consumer消費狀態,一旦Kafka Server端出現延時或是消費狀態丟失,將會影響大量的Consumer。同時,這一設計也提高了Consumer的靈活性,Consumer可以按照自己需要的順序和模式拉取消息進行消費。例如:Consumer可以通過修改其消費者的位置實現針對某些特殊key的消息進行反覆消費,或是跳過某些消息的需求。

3.6 Consumer group

high-level consumer API 中,每個 consumer 都屬於一個 consumer group,每條消息只能被 consumer group 中的一個 Consumer 消費,但可以被多個 consumer group 消費。

3.7 Replica

Kafka對消息進行了冗餘備份,每個Partition分區都可以有多個副本,每一個副本中包含的消息是相同的(但不保證同一時刻下完全相同)。每個分區至少有一個副本,當分區只有一個副本的時候,就只有Leader副本,沒有 Follower。在每個副本集合中,都會選舉出一個副本作為Leader副本,Kafka在不同的場景中會採用不同的選舉策略。Kafka中所有的讀寫請求都由選舉出的Leader副本處理,其他的都作為Follower副本,Follower副本僅僅是從Leader副本中把數據拉取到本地之後,同步更新到自己的Log中。

3.8 ISR

ISR(In-Sync-Replica)集合表示的是目前可用(alive)且消息量與Leader相差不多的副本集合,這是整個副本集合的一個子集。ISR集合中的副本必須滿足下面兩個條件:

(1)副本所在節點必須維持著與ZooKeeper的鏈接。

(2)副本最後一條消息的offset與Leader副本的最後一條消息的offset之間的差值不能超出指定的閾值。

每個分區中的Leader都會維護此分區的ISR集合,寫請求首先由Leader副本處理,之後Follower副本都會從Leader上拉取寫入的消息,這個過程會有一定的延遲,導致Follower副本中保存的消息略少於Leader副本,只要未超出閾值都是可以容忍的。如果一個Follower副本出現異常,比如:宕機,發生長時間GC而導致Kafka僵死或是網絡斷開連接導致長時間沒有拉取消息進行同步,就回違反上面兩個條件,從而被Leader副本踢出ISR集合。當Follower副本從異常中恢復之後,會繼續與Leader副本進行同步,當Follower副本追上(即最後一條消息的offset的差值小於指定閾值)Leader副本的時候,此Follower副本會被Leader副本重新加入到ISR中。

3.9 HW&LEO

HW(High Watermark)和LEO與ISR集合緊密相關。HW標記了一個特殊的offset,當消費者處理消息的時候,只能拉取到HW之前的消息,HW之後的消息對消費者來說是不可見的。與ISR集合類似,HW也是由Leader副本管理的。當ISR集合中全部的Follower副本都拉取HW指定消息進行同步後,Leader副本會遞增HW的值。Kafka官方網站將HW之前的消息的狀態稱為commit,其含義是這些消息在多個副本中同時存在,即使Leader副本損壞,也不會出現數據丟失。

LEO(Log End Offset)是所有的副本都會有的一個offset標記,它指向追加到當前副本的最後一個消息的offset。當生產者向Leader副本追加消息的時候,Leader副本的LEO標記會遞增;當Follower副本成功從Leader副本拉取消息並更新到本地的時候,Follower副本的LEO就會增加。

為了讓讀者更好的理解HW和LEO之間的關係,下面通過一個示例進行分析ISR集合,HW和LEO是如何協調工作的。

kafka架構原理

image

1 Poducer向此Partition推送消息。

2 Leader副本將消息追加到Log中,並遞增其LEO。

3 Follower副本從Leader副本拉取消息並進行同步。

4 Follower副本將拉取到的消息更新到本地Log中,並遞增其LEO。

5 當ISR集合中所有副本都完成了對offset消息的同步,Leader副本會遞增HW。

3.10 ZooKeeper

kafka 通過 zookeeper 來存儲集群的 meta 信息。

對Java微服務、分佈式、高併發、高可用、大型互聯網架構技術、面試經驗交流感興趣的。可以關注我的頭條號,我會在微頭條不定期的發放免費的資料鏈接,這些資料都是從各個技術網站蒐集、整理出來的,如果你有好的學習資料可以私聊發我,我會註明出處之後分享給大家。歡迎分享,歡迎評論,歡迎轉發!


分享到:


相關文章: