掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
作者:sowhat0125 2021-02-01 20:35:49
開發(fā)
架構(gòu)
Kafka Kafka 是一個分布式的基于發(fā)布/訂閱模式的消息隊(duì)列,依靠其強(qiáng)悍的吞吐量,Kafka 主要應(yīng)用于大數(shù)據(jù)實(shí)時處理領(lǐng)域。在數(shù)據(jù)采集、傳輸、存儲的過程中發(fā)揮著舉足輕重的作用。

成都創(chuàng)新互聯(lián)專注于阜城網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供阜城營銷型網(wǎng)站建設(shè),阜城網(wǎng)站制作、阜城網(wǎng)頁設(shè)計、阜城網(wǎng)站官網(wǎng)定制、微信小程序開發(fā)服務(wù),打造阜城網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供阜城網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
本文轉(zhuǎn)載自微信公眾號「sowhat1412」,作者sowhat0125。轉(zhuǎn)載本文請聯(lián)系sowhat1412公眾號。
1 Kafka 簡介
1.1 Kafka 概述
Kafka架構(gòu)
Kafka 是一個分布式的基于發(fā)布/訂閱模式的消息隊(duì)列,依靠其強(qiáng)悍的吞吐量,Kafka 主要應(yīng)用于大數(shù)據(jù)實(shí)時處理領(lǐng)域。在數(shù)據(jù)采集、傳輸、存儲的過程中發(fā)揮著舉足輕重的作用。
1.2 Kafka 優(yōu)點(diǎn)
1.3 Kafka 缺點(diǎn)
1.4 Kafka 架構(gòu)
1.5 ZooKeeper 作用
ZooKeeper 在 Kafka 中有舉足輕重的地位,一般提供如下功能:
1.5.1 Broker 注冊
Broker 是分布式部署并且相互之間相互獨(dú)立,但是需要有一個注冊系統(tǒng)能夠?qū)⒄麄€集群中的Broker管理起來,比如用ZooKeeper。
1.5.2 Topic注冊
在 Kafka 中同一個 Topic 的消息會被分成多個 Partition 并將其分布在多個 Broker 上,這些 Partition 信息及與 Broker 的對應(yīng)關(guān)系也都是由 Zookeeper 在維護(hù),由專門的節(jié)點(diǎn)來記錄。
1.5.3 生產(chǎn)者負(fù)載均衡
同一個Topic消息會被分區(qū)并將其分布在多個Broker上,因此,生產(chǎn)者需要將消息合理地發(fā)送到這些分布式的Broker上。
老式的四層負(fù)載均衡,根據(jù)生產(chǎn)者的IP地址和端口來為其確定一個相關(guān)聯(lián)的Broker。一般一個生產(chǎn)者只會對應(yīng)單個Broker,但實(shí)際系統(tǒng)中的每個生產(chǎn)者產(chǎn)生的消息量及每個Broker的消息存儲量都是不一樣的。
使用 Zookeeper 進(jìn)行負(fù)載均衡,由于每個Broker啟動時,都會完成Broker注冊過程,生產(chǎn)者會通過該節(jié)點(diǎn)的變化來動態(tài)地感知到Broker服務(wù)器列表的變更,這樣就可以實(shí)現(xiàn)動態(tài)的負(fù)載均衡機(jī)制。
1.5.4 消費(fèi)者負(fù)載均衡
Kafka 中的消費(fèi)者同樣需要進(jìn)行負(fù)載均衡來實(shí)現(xiàn)多個消費(fèi)者合理地從對應(yīng)的 Broker 服務(wù)器上接收消息,每個消費(fèi)者分組包含若干消費(fèi)者,每條消息都只會發(fā)送給分組中的一個消費(fèi)者,不同的消費(fèi)者分組消費(fèi)自己特定的Topic下面的消息,互不干擾。
1.5.5 分區(qū) 與 消費(fèi)者 的關(guān)系
Kafka 會為每個 Consumer Group 分配個全局唯一 Group ID,Group 內(nèi)的 Consumer 共享該 ID,Kafka規(guī)定 每個partition信息只能被同組的一個Consumer 消費(fèi),在Zk中記錄partition 跟 Consumer關(guān)系,每個消費(fèi)者一旦確定了對一個消息分區(qū)的消費(fèi)權(quán)力,需要將其Consumer ID 寫入到 Zookeeper 對應(yīng)消息分區(qū)的臨時節(jié)點(diǎn)上。
1.5.6 消息消費(fèi)進(jìn)度 Offset 記錄
Consumer 對指定消息分區(qū)進(jìn)行消費(fèi)的過程中,需要定時地將分區(qū)消息的消費(fèi)進(jìn)度 Offset 記錄到 Zookeeper 上,以便在該 Consumer 進(jìn)行重啟或者其他 Consumer 重新接管該消息分區(qū)的消息消費(fèi)后,能夠從之前的進(jìn)度開始繼續(xù)進(jìn)行消息消費(fèi)。
1.5 7 消費(fèi)者注冊
為讓同一個 Topic 下不同分區(qū)的消息盡量均衡地被多個 Consumer 消費(fèi)而進(jìn)行 Consumer 與消息分區(qū)分配的過程。
Consumer 啟動后在ZK下創(chuàng)建個節(jié)點(diǎn),并且每個 Consumer 會對 Consumer Group 中的 Consumer 的變化注冊監(jiān)聽,目的是為了保證 Consumer 負(fù)載均衡。
Consumer 會對Broker列表監(jiān)聽,發(fā)生變化會進(jìn)行 Consumer 負(fù)載均衡。
2 Kafka 生成過程
2.1 寫入方式
producer采用 push 模式將消息發(fā)布到 broker,每條消息都被 append 到 patition 中,屬于 順序?qū)懘疟P ,順序?qū)懕入S機(jī)寫要起碼提速3個數(shù)量級!
2.2 分區(qū) Partition
2.2.1 Partition 簡介
消息發(fā)送時都被發(fā)送到一個topic,其本質(zhì)就是一個目錄,而topic是由一些 分區(qū)日志 Partition Logs 組成,其組織結(jié)構(gòu)如下圖所示:
Partition發(fā)生
可以看到每個 Partition 中的消息都是有序的,生產(chǎn)的消息被不斷追加到 Partition log 上,其中的每一個消息都被賦予了一個唯一的 offset 值。
消費(fèi)者
通過分區(qū)可以 方便在集群中擴(kuò)展,可以提高并發(fā)。
形象理解:
Kafka 的設(shè)計源自生活,好比為公路運(yùn)輸,不同的起始點(diǎn)和目的地需要修不同高速公路(主題),高速公路上可以提供多條車道(分區(qū)),流量大的公路(主題)多修幾條車道(分區(qū))保證暢通,流量小的公路少修幾條車道避免浪費(fèi)。收費(fèi)站好比消費(fèi)者,車多的時候多開幾個一起收費(fèi)避免堵在路上,車少的時候開幾個讓汽車并道就好了。
2.2.2 分區(qū)原則
我們需要將producer發(fā)送的數(shù)據(jù)封裝成一個ProducerRecord對象。
數(shù)據(jù)封裝
2.3 Kafka 文件存儲機(jī)制
Kafka存儲結(jié)構(gòu)
- 100000000000000000000.index
- 200000000000000000000.log
- 300000000000000170410.index
- 400000000000000170410.log
- 500000000000000239430.index
- 600000000000000239430.log
注意:index 和 log 文件以當(dāng)前segment的第一條消息的 offset 命名。
數(shù)據(jù)查找過程
2.4 如何保證消息順序執(zhí)行
2.4.1 順序錯亂
Kafka 一個 topic,一個 partition,一個 Consumer,但是 Consumer 內(nèi)部進(jìn)行多線程消費(fèi),這樣數(shù)據(jù)也會出現(xiàn)順序錯亂問題。
多線程消費(fèi)
數(shù)據(jù)有順序的數(shù)據(jù)寫入到了不同的 partition 里面,不同的消費(fèi)者去消費(fèi),但是每個 Consumer 的執(zhí)行時間是不固定的,無法保證先讀到消息的 Consumer 一定先完成操作,這樣就會出現(xiàn)消息并沒有按照順序執(zhí)行,造成數(shù)據(jù)順序錯誤。
多個消費(fèi)者
2.4.2 解決辦法
確保同一個消息發(fā)送到同一個 partition,一個topic,一個partition,一個consumer,內(nèi)部單線程消費(fèi)。
單線程消費(fèi)
在1的基礎(chǔ)上,在一個 Consumer 上根據(jù)信息ID映射到不同隊(duì)列,以此加速消費(fèi)。
內(nèi)存隊(duì)列
4 數(shù)據(jù)可靠性
4.1 消息傳遞語義
消息傳遞語義 message delivery semantic ,Kafka 為確保消息在 producer 和 consumer 之間傳輸。有以下三種傳輸保障(delivery guarantee):
理想情況下肯定希望系統(tǒng)的消息傳遞是嚴(yán)格 exactly once,但很難做到。接下來會按照 消息的傳播流程大致說下。
4.2 信息從生產(chǎn)者到 Broker
4.2.1 生產(chǎn)者信息發(fā)送至Broker
大致步驟如下:
對于Leader回復(fù) ack,Kafka 為用戶提供了三種可靠性級別,用戶根據(jù)對可靠性和延遲的要求進(jìn)行權(quán)衡。
producer不等待 broker 的ack,提供了一個最低的延遲,broker接收到還沒有寫入磁盤就已經(jīng)返回,當(dāng)broker故障時有可能丟失數(shù)據(jù),對應(yīng) At Most Once 模式。
但凡沒落盤成功信息就丟失了,一般生產(chǎn)不用。
此乃默認(rèn)值,producer 等待 broker 的 ack,partition 的leader落盤成功后返回ack,如果在follower同步成功之前l(fā)eader故障,那么將會丟失數(shù)據(jù);認(rèn)為leader返回 信息就成功了。
producer 等待 broker 的 ack,partition 的 leader 和 follower (ISR中的)全部落盤成功后才返回 ack。
但如果在 leader 收到信息返回ok,follower 收到信息但是發(fā)送 ack 時 leader 故障,此時生產(chǎn)者會重新給follower 發(fā)送個信息。
對應(yīng) At Least Once 模式。
4.2.2 如何保證冪等性
如果業(yè)務(wù)需要數(shù)據(jù) Exactly Once,在早期的 Kafka 版本中 只能在下游去重,現(xiàn)在引入了個冪等性,意思就是無論生產(chǎn)者發(fā)送多少個重復(fù)消息,Server端只會持久化一條數(shù)據(jù),
At Least Once + 冪等性 = Exactly Once
啟動冪等性,在生產(chǎn)者參數(shù)中 enable.idompotence= true,開啟冪等性的生產(chǎn)者在初始化時候會被分配一個PID,發(fā)送同一個Partition的消息會附帶Sequence Number,Broker會對做緩存,以此來判斷唯一性。但是如果PID重啟就會發(fā)生變化,同時不同partition也具有不同的主鍵,冪等性無法保證跨分區(qū)會話的 Exactly Once。
4.3 Kafka Broker 信息落磁盤
數(shù)據(jù)落盤過程
Kafka Broker 收到信息后,如何落盤是通過 producer.type 來設(shè)定的,一般兩個值。
sync,默認(rèn)模式,數(shù)據(jù)必須最終落盤才算OK。
async,異步模式,數(shù)據(jù)刷新到OS的 Page Cache就返回,此時如果機(jī)器突然出問題,信息就丟失了。
4.4 消費(fèi)者從 Kafka Broker 消費(fèi)數(shù)據(jù)
消費(fèi)數(shù)據(jù)
Consumer 是以 Consumer Group 消費(fèi)者組的方式工作,由一個或者多個消費(fèi)者組成一個組,共同消費(fèi)一個topic。每個分區(qū)在同一時間只能由group中的一個消費(fèi)者讀取,但是多個group可以同時消費(fèi)這個partition。如果一個消費(fèi)者失敗了,那么其他的 group 成員會自動負(fù)載均衡讀取之前失敗的消費(fèi)者讀取的分區(qū)。Consumer Group 從 Broker 拉取消息來消費(fèi)主要分為兩個階段:
獲得數(shù)據(jù),提交 Offset。
開始處理數(shù)據(jù)。
如果你先提交 offset 再處理數(shù)據(jù)可能在處理數(shù)據(jù)時出現(xiàn)異常導(dǎo)致數(shù)據(jù)丟失。而如果你先處理數(shù)據(jù)再提交 offset, 如果提交 offset 失敗可能導(dǎo)致信息重復(fù)消費(fèi)。
PS:
pull 模式不足之處是,如果 kafka 沒有數(shù)據(jù),消費(fèi)者可能會陷入循環(huán)中,一直返回空數(shù)據(jù)。針對這一點(diǎn),Kafka的消費(fèi)者在消費(fèi)數(shù)據(jù)時會傳入一個時長參數(shù) timeout,如果當(dāng)前沒有數(shù)據(jù)可供消費(fèi),consumer會等待一段時間之后再返回,這段時長即為 timeout。
5 Kafka 分區(qū)分配策略
同一個 group.id 中的消費(fèi)者,對于一個 topic 中的多個 partition 中的消息消費(fèi),存在著一定的分區(qū)分配策略。
在 Kafka 中存在著兩種分區(qū)分配策略,通過 partition.assignment.strategy 來設(shè)置。
5.1 RangeAssignor 范圍分區(qū)策略
Range 范圍分區(qū)策略是對每個 topic 而言的。首先對同一個 topic 里面的分區(qū)按照序號進(jìn)行排序,并對消費(fèi)者按照字母順序進(jìn)行排序。假如現(xiàn)在有 10 個分區(qū),3 個消費(fèi)者,排序后的分區(qū)將會是p0~p9。消費(fèi)者排序完之后將會是C1-0、C2-0、C3-0。通過 Partitions數(shù) / Consumer數(shù) 來決定每個消費(fèi)者應(yīng)該消費(fèi)幾個分區(qū)。如果除不盡,那么前面幾個消費(fèi)者將會多消費(fèi) 1 個分區(qū)。
| 消費(fèi)者 | 消費(fèi)的分區(qū) |
|---|---|
| C1-0 | 消費(fèi) p0、1、2、3分區(qū) |
| C2-0 | 消費(fèi) 4、5、6分區(qū) |
| C3-0 | 消費(fèi) 7、8、9分區(qū) |
Range 范圍分區(qū)的弊端:
如上只是針對 1 個 topic 而言,C1-0 消費(fèi)者多消費(fèi)1個分區(qū)影響不是很大。如果有 N 多個 topic,那么針對每個 topic,消費(fèi)者 C1-0 都將多消費(fèi) 1 個分區(qū),topic越多,C1-0 消費(fèi)的分區(qū)會比其他消費(fèi)者明顯多消費(fèi) N 個分區(qū)。這就是 Range 范圍分區(qū)的一個很明顯的弊端了.
5.2 RoundRobinAssignor 輪詢分區(qū)策略
RoundRobin 輪詢分區(qū)策略是把所有的 partition 和所有的 consumer 都列出來,然后按照 hascode 進(jìn)行排序,最后通過輪詢算法來分配 partition 給到各個消費(fèi)者。輪詢分區(qū)分為如下兩種情況:
5.2.1 Consumer Group 內(nèi) Consumer 訂閱信息相同
如果同一消費(fèi)組內(nèi),所有的消費(fèi)者訂閱的消息都是相同的,那么 RoundRobin 策略的分區(qū)分配會是均勻的。
例如同一消費(fèi)者組中,有 3 個消費(fèi)者C0、C1和C2,都訂閱了 2 個主題 t0 和 t1,并且每個主題都有 3 個分區(qū)(p0、p1、p2),那么所訂閱的所以分區(qū)可以標(biāo)識為t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最終分區(qū)分配結(jié)果如下:
| 消費(fèi)者 | 消費(fèi)的分區(qū) |
|---|---|
| C0 | 消費(fèi) t0p0、t1p0 分區(qū) |
| C1 | 消費(fèi) t0p1、t1p1 分區(qū) |
| C2 | 消費(fèi) t0p2、t1p2 分區(qū) |
5.2.1 Consumer Group 內(nèi) Consumer 訂閱信息不相同
同一消費(fèi)者組內(nèi),所訂閱的消息是不相同的,那么分區(qū)分配就不是完全的輪詢分配,有可能會導(dǎo)致分區(qū)分配的不均勻。如果某個消費(fèi)者沒有訂閱消費(fèi)組內(nèi)的某個 topic,那么在分配分區(qū)的時候,此消費(fèi)者將不會分配到這個 topic 的任何分區(qū)。
例如同一消費(fèi)者組中有3個消費(fèi)者C0、C1、C2,他們共訂閱了 3 個主題t0、t1、t2,這 3 個主題分別有 1、2、3 個分區(qū)(即t0有1個分區(qū)(p0),t1有2個分區(qū)(p0、p1),t2有3個分區(qū)(p0、p1、p2)),即整個消費(fèi)者所訂閱的所有分區(qū)可以標(biāo)識為 t0p0、t1p0、t1p1、t2p0、t2p1、t2p2。然后消費(fèi)者 C0 訂閱的是主題t0,消費(fèi)者C1訂閱的是主題t0和t1,消費(fèi)者C2訂閱的是主題t0、t1和t2,最終分區(qū)分配結(jié)果如下:
| 消費(fèi)者 | 消費(fèi)的分區(qū) |
|---|---|
| C0 | 消費(fèi) t0p0 分區(qū) |
| C1 | 消費(fèi) t1p0 分區(qū) |
| C2 | 消費(fèi) t1p1、 t2p0、 t2p1、 t2p2 分區(qū) |
6 Kafka 高效讀寫
Kafka 可支持百萬 TPS 跟如下幾個特性有關(guān)。
6.1 順序讀寫數(shù)據(jù)
信息存儲在硬盤中,硬盤由很多盤片組成,顯微鏡觀察盤片會看見盤片表面凹凸不平,凸起的地方被磁化代表數(shù)字1,凹的地方是沒有被磁化代表數(shù)字0,因此硬盤可以以二進(jìn)制來存儲表示文字、圖片等信息。
磁盤平面圖
上圖是硬盤的實(shí)際圖,可能無法理解內(nèi)部構(gòu)造,我們來看個形象的圖:
磁盤內(nèi)部圖
扇區(qū):硬盤的最小讀寫單元
塊/簇:是操作系統(tǒng)針對硬盤讀寫的最小單元
page:是內(nèi)存與操作系統(tǒng)之間操作的最小單元。
一次訪盤的讀/寫請求完成過程由三個動作組成:
可以發(fā)現(xiàn)讀取主要耗時是在前兩個,如果我順序讀取則尋道跟旋轉(zhuǎn)延遲只用一次即可。而如果隨機(jī)讀取呢則可能經(jīng)歷多次尋道跟旋轉(zhuǎn)延遲,兩者相差幾乎 3個數(shù)量級。
隨機(jī)跟順序讀寫在磁盤跟內(nèi)存中
6.2 Memory Mapped Files 內(nèi)存映射文件
內(nèi)存映射
6.3 Zero Copy
6.3.1 直接內(nèi)存存取 DMA
CPU 發(fā)出指令操作 IO 來進(jìn)行讀寫操作,大部分情況下其實(shí)只是把數(shù)據(jù)讀取到內(nèi)存,然后從內(nèi)存?zhèn)鞯絀O即可,所以數(shù)據(jù)其實(shí)可以不經(jīng)過CPU的。
Direct Memory Access 的出現(xiàn)就是為批量數(shù)據(jù)的輸入/輸出而提速的。DMA 是指外部設(shè)備不通過CPU而直接與系統(tǒng)內(nèi)存交換數(shù)據(jù)的接口技術(shù)。這樣數(shù)據(jù)的傳送速度就取決于存儲器和外設(shè)的工作速度。
如果數(shù)據(jù)傳輸?shù)臅r候只用到了 DMA 傳輸而沒經(jīng)過 CPU 復(fù)制數(shù)據(jù),則我們稱之為零拷貝 Zero Copy。用了 Zero Copy 技術(shù)耗時性能起碼減半。
6.3.2 Kafka 讀寫對比
零拷貝
如上黑色流程是沒用 Zero Copy 技術(shù)流程:
紅色流程是用 Zero Copy 技術(shù)流程:
6.4 Batch Deal
消費(fèi)者拉取數(shù)據(jù)的時候,Kafka 不是一個一個的來送數(shù)據(jù)的,而是批量發(fā)送來處理的,這樣可以節(jié)省網(wǎng)絡(luò)傳輸,增大系統(tǒng)的TPS,不過也有個缺點(diǎn)就是,我們的數(shù)據(jù)不是真正的實(shí)時處理的,而真正的實(shí)時還是要看 Flink。
7 參考
Kafka 為什么要分區(qū) :https://www.zhihu.com/question/28925721
關(guān)于磁盤讀取:https://blog.csdn.net/holybin/article/details/21175781
Kafka百萬TPS:https://mp.weixin.qq.com/s/Fb1cW0oN7xYeb1oI2ixtgQ

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流