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