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

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

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

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

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

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

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