加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    •  
    • 1、Kafka 簡(jiǎn)介
    •  
    • 2、Kafka 生成過(guò)程
    •  
    • 4、數(shù)據(jù)可靠性
    •  
    • 5、Kafka 分區(qū)分配策略
    • 6、Kafka 高效讀寫
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

六問(wèn) Kafka 為啥那么牛!

2021/02/02
231
閱讀需 34 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

 

1、Kafka 簡(jiǎn)介

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ì)開(kāi)發(fā)的一個(gè)開(kāi)源消息系統(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ù)傳輸開(kāi)銷,提高吞高量。

支持多種模式的消息,消息是基于磁盤實(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)度開(kāi)始繼續(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)聽(tīng),目的是為了保證 Consumer 負(fù)載均衡。

Consumer 會(huì)對(duì) Broker 列表監(jiān)聽(tīng),發(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í)候多開(kāi)幾個(gè)一起收費(fèi)避免堵在路上,車少的時(shí)候開(kāi)幾個(gè)讓汽車并道就好了。

2.2.2 分區(qū)原則

我們需要將 producer 發(fā)送的數(shù)據(jù)封裝成一個(gè) ProducerRecord 對(duì)象。

數(shù)據(jù)封裝

 

指明 partition 的情況下,直接將指明的值直接作為 partiton 值。

沒(méi)有指明 partition 值但有 key 的情況下,將 key 的 hash 值與 topic 的 partition 數(shù)進(jìn)行取余得到 partition 值。

既沒(méi)有 partition 值又沒(méi)有 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)題。

  1. 多線程消費(fèi)

    數(shù)據(jù)有順序的數(shù)據(jù)寫入到了不同的 partition 里面,不同的消費(fèi)者去消費(fèi),但是每個(gè) Consumer 的執(zhí)行時(shí)間是不固定的,無(wú)法保證先讀到消息的 Consumer 一定先完成操作,這樣就會(huì)出現(xiàn)消息并沒(méi)有按照順序執(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 接收到還沒(méi)有寫入磁盤就已經(jīng)返回,當(dāng) broker 故障時(shí)有可能丟失數(shù)據(jù),對(duì)應(yīng) At Most Once 模式。

但凡沒(méi)落盤成功信息就丟失了,一般生產(chǎn)不用。

request.required.acks  = 1

此乃默認(rèn)值,producer 等待 broker 的 ack,partition 的leader 落盤成功后返回 ack,如果在 follower 同步成功之前 leader 故障,那么將會(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,開(kāi)啟冪等性的生產(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。

開(kāi)始處理數(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 沒(méi)有數(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)前沒(méi)有數(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)者沒(mé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ì)看見(jiàn)盤片表面凹凸不平,凸起的地方被磁化代表數(shù)字 1,凹的地方是沒(méi)有被磁化代表數(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ū)開(kāi)始寫數(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)作組成:

尋道:磁頭從開(kāi)始移動(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)鞯?IO 即可,所以數(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 傳輸而沒(méi)經(jīng)過(guò) CPU 復(fù)制數(shù)據(jù),則我們稱之為零拷貝 Zero Copy。用了 Zero Copy 技術(shù)耗時(shí)性能起碼減半。

6.3.2 Kafka 讀寫對(duì)比

零拷貝

 

如上黑色流程是沒(méi)用 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)于磁盤讀?。篽ttps://blog.csdn.net/holybin/article/details/21175781

Kafka 百萬(wàn) TPS:https://mp.weixin.qq.com/s/Fb1cW0oN7xYeb1oI2ixtgQ


RabbitMQ 高頻考點(diǎn)

2021-01-27

Hadoop 超燃之路

2021-01-25

20 張圖帶你到 HBase 的世界遨游

2021-01-20

講解 Zookeeper 的五個(gè)核心知識(shí)點(diǎn)

2021-01-15

淺談大數(shù)據(jù)中的 2PC、3PC、Paxos、Raft、ZAB

2020-11-03

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

【sowhat1412】 公眾號(hào)維護(hù)者,CSDN博客專家,擁有百萬(wàn)閱讀,上萬(wàn)粉絲,周榜前20博主,碩士畢業(yè),待過(guò)二線廠,目前在研究院工作。專注于分享Java、Python、大數(shù)據(jù)、知識(shí)圖譜等相關(guān)技術(shù)。經(jīng)常送書籍,歡迎關(guān)注,號(hào)內(nèi)學(xué)習(xí)2T學(xué)習(xí)面試資料等你來(lái)取。