九張圖帶你了解Kafka
現(xiàn)在,每個公司都在互聯(lián)網(wǎng)系統(tǒng)中使用Kafka。 Kafka似乎是解決分布式并提高系統(tǒng)吞吐量的最佳松耦合解決方案之一。
我大約6年前開始使用Kafka。 當時,我在Aerohive工作。 為了解決企業(yè)wifi設(shè)備帶來的大量日志,傳統(tǒng)的消息傳遞系統(tǒng)RabbitMQ和ActiveMQ已經(jīng)不堪重負。
此時,Kafka誕生了(2012年),并提供了一個完美的解決方案。
重要要點:
- 解釋消息隊列及其優(yōu)勢
- 解釋卡夫卡中的組成部分(經(jīng)紀人,生產(chǎn)者,消費者,消費者組)
- 解釋為什么卡夫卡這么快
什么是消息系統(tǒng)?
在了解Kafka之前,如果您不知道什么是Message Queue,則需要添加它。 如果您已經(jīng)知道,則可以跳到下一段。
> Morden Distributed System
如上圖所述,Message Queue是一個在兩個系統(tǒng)之間傳輸和存儲消息的中間件。 其外觀具有以下優(yōu)點:
- 去耦:只要您確保雙方遵守相同的接口約束,就可以獨立擴展或修改雙方的處理。
- 冗余:消息隊列將數(shù)據(jù)保留到完成處理為止,從而避免了數(shù)據(jù)丟失的風險。 在許多消息隊列采用的"插入-獲取-刪除"范式中,從隊列中刪除消息之前,您的處理系統(tǒng)需要清楚地表明該消息已被處理,以確保您的數(shù)據(jù)安全保存。 完成使用它。
- 可伸縮性:由于消息隊列使您的處理脫鉤,因此,只要添加其他處理,就很容易增加消息入隊和處理的頻率。
- 靈活性和高峰處理能力:在流量急劇增加的情況下,應(yīng)用程序仍然需要繼續(xù)發(fā)揮作用,但是這種突發(fā)流量并不是標準的。 毫無疑問,以能夠處理高峰訪問為標準來投資資源是巨大的浪費。 消息隊列的使用可使關(guān)鍵組件承受突然的訪問壓力,而不會由于意外的過載請求而完全崩潰。
- 可恢復性:當系統(tǒng)的某些部分發(fā)生故障時,它不會影響整個系統(tǒng)。 消息隊列減少了進程之間的耦合,因此,即使處理消息的進程掛斷了,恢復系統(tǒng)后仍可以處理添加到隊列中的消息。
- 順序保證:在大多數(shù)使用情況下,數(shù)據(jù)處理的順序至關(guān)重要。 大多數(shù)消息隊列最初都是經(jīng)過排序的,可以保證數(shù)據(jù)將按特定順序進行處理。 (Kafka保證分區(qū)中消息的順序)
- 緩沖:有助于控制和優(yōu)化通過系統(tǒng)的數(shù)據(jù)流速度,并解決生產(chǎn)消息和消耗消息的不一致處理速度。
- 異步通信:很多時候,用戶不希望也不需要立即處理消息。 消息隊列提供了一種異步處理機制,該機制允許用戶將消息放入隊列,但不能快速處理。 將所需數(shù)量的消息放入隊列,然后在需要時進行處理。
同時,我認為最大的缺點是復雜性,其優(yōu)點完全可以忽略不計。
Kafka如何運作?
對于Kafka而言,從獨立的角度來看,其中包括生產(chǎn)者,消費者和經(jīng)紀人。
- 生產(chǎn)者負責將消息發(fā)送到代理固定主題
- 代理維護一組主題并管理該主題中的分區(qū)
- 消費者,負責從經(jīng)紀人的相應(yīng)主題中提取消息
> Kafka components
如圖所示,不同的生產(chǎn)者可以將消息發(fā)送到多個主題的多個分區(qū),而消費者也可以從各種主題中消費。
生產(chǎn)者和消費者完全孤立。
在此設(shè)計中,它充分體現(xiàn)了去耦,靈活性和峰值處理能力,訂單保證和異步通信。
Kafka如何在分布式環(huán)境中工作?
1. 集群
多個代理和副本。
- 副本,分區(qū)副本,以確保分區(qū)的高可用性
- 領(lǐng)導者,副本,生產(chǎn)者和使用者中的角色僅與領(lǐng)導者互動
- 追隨者中的一個角色,副本以復制領(lǐng)導者中的數(shù)據(jù)。

Kafka如何保證冗余,可恢復性和高可用性?
即使某些節(jié)點發(fā)生故障,復制也可以提供高可用性:
- 生產(chǎn)者可以繼續(xù)發(fā)布消息
- 使用者可以繼續(xù)接收消息。 有兩種方案可確保強而一致的數(shù)據(jù)復制:主備份復制和基于仲裁的復制。 兩種方案都需要選舉一位領(lǐng)導者,其他人則作為跟隨者。 所有寫操作都發(fā)送給領(lǐng)導者,然后領(lǐng)導者將消息發(fā)送給跟隨者。
基于仲裁的復制可以使用筏和Paxos等算法,例如Zookeeper,Google Spanner等。在2n +1個節(jié)點的情況下,最多可以容忍n個節(jié)點故障。
僅在成功接收到消息后,基于主數(shù)據(jù)庫的復制以及其他主數(shù)據(jù)庫和備份的寫入操作才能成功。 對于n個節(jié)點,最多可以容忍n-1個節(jié)點故障,例如Microsoft的PacifiaA。
這兩種方法各有優(yōu)缺點。
- 基于仲裁的延遲可能比主備份更好,因為基于仲裁的方法僅需要一些節(jié)點即可成功寫入以返回。
- 在相同數(shù)量的節(jié)點下,基于主備份的復制可以承受更多的節(jié)點故障,并且只要一個節(jié)點處于活動狀態(tài)就可以正常工作。
- 在有兩個節(jié)點的情況下,主備份可以提供容錯能力,基于仲裁的方法至少需要三個節(jié)點。
Kafka采用第二種方法,即主從模式,該方法主要基于容錯能力,并且在兩個節(jié)點的情況下也可以提供高可用性。
如果節(jié)點很慢怎么辦?
首先,這種情況很少發(fā)生。 如果發(fā)生這種情況,您可以設(shè)置超時參數(shù)來處理這種情況。
Kafka的復制適用于分區(qū)。
例如,在上圖中,有四個代理,一個主題和兩個分區(qū)。 復制因子是三。 當生產(chǎn)者發(fā)送消息時,它將選擇一個分區(qū),例如topic1-part1分區(qū),將消息發(fā)送到該分區(qū)的領(lǐng)導者,broker2,broker3將拉出消息,消息被拉出后,從屬將發(fā)送ack到 主機,這次主機僅提交此日志。
在此過程中,生產(chǎn)者有兩種選擇:
- 一種是等待所有副本被成功提取,然后生產(chǎn)者盤收到成功的響應(yīng)。
- 另一種是等待領(lǐng)導者成功編寫并獲得成功的響應(yīng)。
在第一個中,您可以確保在異常情況下不會丟失消息,但是延遲會減少。 后者的等待時間已大大改善,但是一旦出現(xiàn)異常情況,從屬服務(wù)器將無法在領(lǐng)導掛起之前提取最新消息。 在這種情況下,可能會丟失該消息。
2. 客戶群

消費者使用消費者組名稱標記自己,并且發(fā)布到主題的每條記錄都會傳遞到每個訂閱消費者組中的一個消費者實例。 使用者實例可以在單獨的進程中或在單獨的機器上。
如果所有使用者實例都具有相同的使用者組,那么將在這些使用者實例上有效地平衡記錄。
如果所有使用者實例具有不同的使用者組,則每條記錄將廣播到所有使用者進程形成正式文件
簡而言之,消費者群體是Kafka生態(tài)系統(tǒng)中的真正消費者。
3. 控制者

上圖是2015年Kafka Controller的設(shè)計圖。 Controller和ZK共同構(gòu)建了Kafka的高層架構(gòu),該架構(gòu)主要完成以下任務(wù):
- 管理經(jīng)紀人和消費者的動態(tài)加入和離開。
- 觸發(fā)負載平衡。 當經(jīng)紀人或使用者加入或離開時,將觸發(fā)負載均衡算法,從而為一個使用者組中的多個使用者進行訂閱負載均衡。
- 維護每個分區(qū)的消耗關(guān)系和消耗信息。
為什么Kafka這么快?
Kafka中有一個過程,其中大量網(wǎng)絡(luò)數(shù)據(jù)被持久保存到磁盤(生產(chǎn)者到代理),并且磁盤文件通過網(wǎng)絡(luò)發(fā)送(經(jīng)紀人到消費者)。
此過程的性能直接影響Kafka的整體吞吐量。
1. 零復制

上圖的左側(cè)是傳統(tǒng)的四個副本和四個上下文切換。
- 首先,通過系統(tǒng)調(diào)用將文件數(shù)據(jù)讀入內(nèi)核狀態(tài)緩沖區(qū)(DMA復制)
- 然后,應(yīng)用程序?qū)?nèi)存狀態(tài)緩沖區(qū)數(shù)據(jù)讀入用戶狀態(tài)緩沖區(qū)(CPU副本)
- 接下來,用戶程序在通過套接字發(fā)送數(shù)據(jù)時讀取用戶狀態(tài)緩沖區(qū)數(shù)據(jù)。復制到內(nèi)核狀態(tài)緩沖區(qū)(CPU復制)
- 最后,通過DMA復制將數(shù)據(jù)復制到NIC緩沖區(qū)。 同時,它伴隨著四個上下文切換。
在上圖的右側(cè),Kafka使用Linux 2.4+內(nèi)核sendfile系統(tǒng)調(diào)用來實現(xiàn)零復制。
- 數(shù)據(jù)通過DMA復制到內(nèi)核狀態(tài)緩沖區(qū)
- 它通過DMA直接復制到NIC緩沖區(qū),而無需CPU復制
因為sendfile調(diào)用完成了整個文件讀取網(wǎng)絡(luò)的傳輸,所以整個過程只有兩個上下文切換,因此性能大大提高了。
準確地說,Kafka的數(shù)據(jù)傳輸是通過TransportLayer完成的,其子類PlaintextTransportLayer通過Java NIO的FileChannel的transferTo和transferFrom方法實現(xiàn)了零復制。
2. 順序訪問
> Compare
上圖顯示,即使順序讀取磁盤,順序訪問的巨大優(yōu)勢也比基于內(nèi)存的隨機訪問要好。
Kafka中的每條消息都會被追加,并且不會從中間寫入或刪除消息,以確保順序訪問磁盤。
即使是順序讀取和寫入,過多的小型IO操作也會導致磁盤瓶頸,并且這次變成了隨機讀取和寫入。
Kafka的策略是匯總消息并分批發(fā)送,以最大程度地減少對磁盤的訪問。 因此,Kafka的主題和分區(qū)的數(shù)量不應(yīng)過多。
通常,經(jīng)過64個主題/分區(qū)之后,Kafka的性能將急劇下降。
3. 段日志

- Kafka使用該主題來管理消息。 每個主題包含多個部分,每個部分對應(yīng)一個邏輯日志,并且由多個部分組成。
- 多個消息存儲在每個段中。 它的邏輯位置決定了消息ID,即消息ID可以直接定位到消息的存儲位置,從而避免了ID到位置的附加映射。
- 每個部分對應(yīng)于內(nèi)存中的一個索引,并記錄每個段中第一條消息的偏移量。
- 發(fā)布者發(fā)送給特定主題的消息將平均分配到多個部分(隨機或根據(jù)用戶指定的回調(diào)函數(shù)),代理接收已發(fā)布的消息并將消息添加到相應(yīng)部分的最后一段。 當段上的消息數(shù)達到配置的值或消息發(fā)布時間超過閾值時,段上的消息將刷新到磁盤,只有刷新到磁盤的消息訂閱者才能訂閱該消息。 段達到特定大小后,將不再有數(shù)據(jù)寫入該段,代理將創(chuàng)建一個新段。
這種分區(qū)分割和索引設(shè)計不僅提高了數(shù)據(jù)讀取的效率,而且還提高了數(shù)據(jù)操作的并行性。
4. 高性能Broker

Kafka在Broker中的設(shè)計也是其如此之快的原因之一。
首先,客戶端發(fā)送的所有請求都將發(fā)送到接受器。 代理中將默認有三個線程。 這三個線程稱為處理器。
接受者將不會對客戶的請求進行任何處理,而是直接對其進行封裝。 將socketChannel發(fā)送到這些處理器以形成隊列。
發(fā)送的方法是輪詢,即先發(fā)送到第一個處理器,然后再發(fā)送到第二個,第三個處理器,然后再返回到第一個處理器。 當使用者線程使用這些socketChannel時,它將獲取請求請求,并且數(shù)據(jù)將伴隨這些請求請求。
默認情況下,線程池中有八個線程。 這些線程用于處理請求和解析請求。 如果請求是書面請求,則將其寫入磁盤。 如果讀取,則返回結(jié)果。 處理器將從響應(yīng)中讀取響應(yīng)數(shù)據(jù),然后將其返回給客戶端。
這是Kafka的三層網(wǎng)絡(luò)架構(gòu)。
因此,如果我們需要增強和調(diào)整Kafka,增加處理器并增加線程池中的處理線程,則可以達到效果。 考慮到處理器生成請求的速度過快,并且線程數(shù)量不足以及時處理請求,因此請求和響應(yīng)實際上是一種緩存效果。
總結(jié)
我希望本文對您了解和初步了解Kafka,它具有的組件以及為什么可以實現(xiàn)如此高的性能有所幫助。
Kafka在現(xiàn)代高并發(fā)系統(tǒng)體系結(jié)構(gòu)中扮演著至關(guān)重要的角色,并且仍在快速發(fā)展,例如流媒體。
本文僅從概念和簡單設(shè)計原理方面說明Kafka。 僅僅掌握它是不夠的。
如果您需要更深入的分析,請參閱官方文檔。
謝謝閱讀!