<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從面試角度詳解Kafka

          共 22845字,需瀏覽 46分鐘

           ·

          2021-07-12 18:03

          Kafka 是一個優(yōu)秀的分布式消息中間件,許多系統(tǒng)中都會使用到 Kafka 來做消息通信。對分布式消息系統(tǒng)的了解和使用幾乎成為一個開發(fā)人員必備的技能。

          思維導圖

          講一講分布式消息中間件

          問題

          • 什么是分布式消息中間件?
          • 消息中間件的作用是什么?
          • 消息中間件的使用場景是什么?
          • 消息中間件選型?
          消息隊列

          分布式消息是一種通信機制,和 RPC、HTTP、RMI 等不一樣,消息中間件采用分布式中間代理的方式進行通信。如圖所示,采用了消息中間件之后,上游業(yè)務系統(tǒng)發(fā)送消息,先存儲在消息中間件,然后由消息中間件將消息分發(fā)到對應的業(yè)務模塊應用(分布式生產(chǎn)者 - 消費者模式)。這種異步的方式,減少了服務之間的耦合程度。

          架構

          定義消息中間件:

          • 利用高效可靠的消息傳遞機制進行平臺無關的數(shù)據(jù)交流
          • 基于數(shù)據(jù)通信,來進行分布式系統(tǒng)的集成
          • 通過提供消息傳遞和消息排隊模型,可以在分布式環(huán)境下擴展進程間的通信

          在系統(tǒng)架構中引用額外的組件,必然提高系統(tǒng)的架構復雜度和運維的難度,那么在系統(tǒng)中使用分布式消息中間件有什么優(yōu)勢呢?消息中間件在系統(tǒng)中起的作用又是什么呢?

          • 解耦
          • 冗余(存儲)
          • 擴展性
          • 削峰
          • 可恢復性
          • 順序保證
          • 緩沖
          • 異步通信

          下面是常見的幾種分布式消息系統(tǒng)的對比:

          選擇

          答案關鍵字

          • 什么是分布式消息中間件?通信,隊列,分布式,生產(chǎn)消費者模式。
          • 消息中間件的作用是什么?解耦、峰值處理、異步通信、緩沖。
          • 消息中間件的使用場景是什么?異步通信,消息存儲處理。
          • 消息中間件選型?語言,協(xié)議、HA、數(shù)據(jù)可靠性、性能、事務、生態(tài)、簡易、推拉模式。

          Kafka 基本概念和架構

          問題

          • 簡單講下 Kafka 的架構?
          • Kafka 是推模式還是拉模式,推拉的區(qū)別是什么?
          • Kafka 如何廣播消息?
          • Kafka 的消息是否是有序的?
          • Kafka 是否支持讀寫分離?
          • Kafka 如何保證數(shù)據(jù)高可用?
          • Kafka 中 zookeeper 的作用?
          • 是否支持事務?
          • 分區(qū)數(shù)是否可以減少?

          Kafka 架構中的一般概念:

          架構
          • Producer:生產(chǎn)者,也就是發(fā)送消息的一方。生產(chǎn)者負責創(chuàng)建消息,然后將其發(fā)送到 Kafka。
          • Consumer:消費者,也就是接受消息的一方。消費者連接到 Kafka 上并接收消息,進而進行相應的業(yè)務邏輯處理。
          • Consumer Group:一個消費者組可以包含一個或多個消費者。使用多分區(qū) + 多消費者方式可以極大提高數(shù)據(jù)下游的處理速度,同一消費組中的消費者不會重復消費消息,同樣的,不同消費組中的消費者消息消息時互不影響。Kafka 就是通過消費組的方式來實現(xiàn)消息 P2P 模式和廣播模式。
          • Broker:服務代理節(jié)點。Broker 是 Kafka 的服務節(jié)點,即 Kafka 的服務器。
          • Topic:Kafka 中的消息以 Topic 為單位進行劃分,生產(chǎn)者將消息發(fā)送到特定的 Topic,而消費者負責訂閱 Topic 的消息并進行消費。
          • Partition:Topic 是一個邏輯的概念,它可以細分為多個分區(qū),每個分區(qū)只屬于單個主題。同一個主題下不同分區(qū)包含的消息是不同的,分區(qū)在存儲層面可以看作一個可追加的日志(Log)文件,消息在被追加到分區(qū)日志文件的時候都會分配一個特定的偏移量(offset)。
          • Offset:offset 是消息在分區(qū)中的唯一標識,Kafka 通過它來保證消息在分區(qū)內的順序性,不過 offset 并不跨越分區(qū),也就是說,Kafka 保證的是分區(qū)有序性而不是主題有序性。
          • Replication:副本,是 Kafka 保證數(shù)據(jù)高可用的方式,Kafka 同一 Partition 的數(shù)據(jù)可以在多 Broker 上存在多個副本,通常只有主副本對外提供讀寫服務,當主副本所在 broker 崩潰或發(fā)生網(wǎng)絡一場,Kafka 會在 Controller 的管理下會重新選擇新的 Leader 副本對外提供讀寫服務。
          • Record:實際寫入 Kafka 中并可以被讀取的消息記錄。每個 record 包含了 key、value 和 timestamp。

          Kafka Topic Partitions Layout

          主題

          Kafka 將 Topic 進行分區(qū),分區(qū)可以并發(fā)讀寫。

          Kafka Consumer Offset

          consumer offset

          zookeeper

          zookeeper
          • Broker 注冊:Broker 是分布式部署并且之間相互獨立,Zookeeper 用來管理注冊到集群的所有 Broker 節(jié)點。
          • Topic 注冊:在 Kafka 中,同一個 Topic 的消息會被分成多個分區(qū)并將其分布在多個 Broker 上,這些分區(qū)信息及與 Broker 的對應關系也都是由 Zookeeper 在維護
          • 生產(chǎn)者負載均衡:由于同一個 Topic 消息會被分區(qū)并將其分布在多個 Broker 上,因此,生產(chǎn)者需要將消息合理地發(fā)送到這些分布式的 Broker 上。
          • 消費者負載均衡:與生產(chǎn)者類似,Kafka 中的消費者同樣需要進行負載均衡來實現(xiàn)多個消費者合理地從對應的 Broker 服務器上接收消息,每個消費者分組包含若干消費者,每條消息都只會發(fā)送給分組中的一個消費者,不同的消費者分組消費自己特定的 Topic 下面的消息,互不干擾。

          答案關鍵字

          • 簡單講下 Kafka 的架構?

            Producer、Consumer、Consumer Group、Topic、Partition

          • Kafka 是推模式還是拉模式,推拉的區(qū)別是什么?

            Kafka Producer 向 Broker 發(fā)送消息使用 Push 模式,Consumer 消費采用的 Pull 模式。拉取模式,讓 consumer 自己管理 offset,可以提供讀取性能

          • Kafka 如何廣播消息?

            Consumer group

          • Kafka 的消息是否是有序的?

            Topic 級別無序,Partition 有序

          • Kafka 是否支持讀寫分離?

            不支持,只有 Leader 對外提供讀寫服務

          • Kafka 如何保證數(shù)據(jù)高可用?

            副本,ack,HW

          • Kafka 中 zookeeper 的作用?

            集群管理,元數(shù)據(jù)管理

          • 是否支持事務?

            0.11 后支持事務,可以實現(xiàn)”exactly once“

          • 分區(qū)數(shù)是否可以減少?

            不可以,會丟失數(shù)據(jù)

          Kafka 使用

          問題

          • Kafka 有哪些命令行工具?你用過哪些?
          • Kafka Producer 的執(zhí)行過程?
          • Kafka Producer 有哪些常見配置?
          • 如何讓 Kafka 的消息有序?
          • Producer 如何保證數(shù)據(jù)發(fā)送不丟失?
          • 如何提升 Producer 的性能?
          • 如果同一 group 下 consumer 的數(shù)量大于 part 的數(shù)量,kafka 如何處理?
          • Kafka Consumer 是否是線程安全的?
          • 講一下你使用 Kafka Consumer 消費消息時的線程模型,為何如此設計?
          • Kafka Consumer 的常見配置?
          • Consumer 什么時候會被踢出集群?
          • 當有 Consumer 加入或退出時,Kafka 會作何反應?
          • 什么是 Rebalance,何時會發(fā)生 Rebalance?

          命令行工具

          Kafka 的命令行工具在 Kafka 包的/bin目錄下,主要包括服務和集群管理腳本,配置腳本,信息查看腳本,Topic 腳本,客戶端腳本等。

          • kafka-configs.sh:配置管理腳本
          • kafka-console-consumer.sh:kafka 消費者控制臺
          • kafka-console-producer.sh:kafka 生產(chǎn)者控制臺
          • kafka-consumer-groups.sh:kafka 消費者組相關信息
          • kafka-delete-records.sh:刪除低水位的日志文件
          • kafka-log-dirs.sh:kafka 消息日志目錄信息
          • kafka-mirror-maker.sh:不同數(shù)據(jù)中心 kafka 集群復制工具
          • kafka-preferred-replica-election.sh:觸發(fā) preferred replica 選舉
          • kafka-producer-perf-test.sh:kafka 生產(chǎn)者性能測試腳本
          • kafka-reassign-partitions.sh:分區(qū)重分配腳本
          • kafka-replica-verification.sh:復制進度驗證腳本
          • kafka-server-start.sh:啟動 kafka 服務
          • kafka-server-stop.sh:停止 kafka 服務
          • kafka-topics.sh:topic 管理腳本
          • kafka-verifiable-consumer.sh:可檢驗的 kafka 消費者
          • kafka-verifiable-producer.sh:可檢驗的 kafka 生產(chǎn)者
          • zookeeper-server-start.sh:啟動 zk 服務
          • zookeeper-server-stop.sh:停止 zk 服務
          • zookeeper-shell.sh:zk 客戶端

          我們通常可以使用kafka-console-consumer.shkafka-console-producer.sh腳本來測試 Kafka 生產(chǎn)和消費,kafka-consumer-groups.sh可以查看和管理集群中的 Topic,kafka-topics.sh通常用于查看 Kafka 的消費組情況。

          Kafka Producer

          Kafka producer 的正常生產(chǎn)邏輯包含以下幾個步驟:

          1. 配置生產(chǎn)者客戶端參數(shù)常見生產(chǎn)者實例。
          2. 構建待發(fā)送的消息。
          3. 發(fā)送消息。
          4. 關閉生產(chǎn)者實例。

          Producer 發(fā)送消息的過程如下圖所示,需要經(jīng)過攔截器序列化器分區(qū)器,最終由累加器批量發(fā)送至 Broker。

          producer

          Kafka Producer 需要以下必要參數(shù):

          • bootstrap.server:指定 Kafka 的 Broker 的地址
          • key.serializer:key 序列化器
          • value.serializer:value 序列化器

          常見參數(shù):

          • batch.num.messages

            默認值:200,每次批量消息的數(shù)量,只對 asyc 起作用。

          • request.required.acks

            默認值:0,0 表示 producer 毋須等待 leader 的確認,1 代表需要 leader 確認寫入它的本地 log 并立即確認,-1 代表所有的備份都完成后確認。只對 async 模式起作用,這個參數(shù)的調整是數(shù)據(jù)不丟失和發(fā)送效率的 tradeoff,如果對數(shù)據(jù)丟失不敏感而在乎效率的場景可以考慮設置為 0,這樣可以大大提高 producer 發(fā)送數(shù)據(jù)的效率。

          • request.timeout.ms

            默認值:10000,確認超時時間。

          • partitioner.class

            默認值:kafka.producer.DefaultPartitioner,必須實現(xiàn) kafka.producer.Partitioner,根據(jù) Key 提供一個分區(qū)策略。有時候我們需要相同類型的消息必須順序處理,這樣我們就必須自定義分配策略,從而將相同類型的數(shù)據(jù)分配到同一個分區(qū)中。

          • producer.type

            默認值:sync,指定消息發(fā)送是同步還是異步。異步 asyc 成批發(fā)送用 kafka.producer.AyncProducer, 同步 sync 用 kafka.producer.SyncProducer。同步和異步發(fā)送也會影響消息生產(chǎn)的效率。

          • compression.topic

            默認值:none,消息壓縮,默認不壓縮。其余壓縮方式還有,"gzip"、"snappy"和"lz4"。對消息的壓縮可以極大地減少網(wǎng)絡傳輸量、降低網(wǎng)絡 IO,從而提高整體性能。

          • compressed.topics

            默認值:null,在設置了壓縮的情況下,可以指定特定的 topic 壓縮,未指定則全部壓縮。

          • message.send.max.retries

            默認值:3,消息發(fā)送最大嘗試次數(shù)。

          • retry.backoff.ms

            默認值:300,每次嘗試增加的額外的間隔時間。

          • topic.metadata.refresh.interval.ms

            默認值:600000,定期的獲取元數(shù)據(jù)的時間。當分區(qū)丟失,leader 不可用時 producer 也會主動獲取元數(shù)據(jù),如果為 0,則每次發(fā)送完消息就獲取元數(shù)據(jù),不推薦。如果為負值,則只有在失敗的情況下獲取元數(shù)據(jù)。

          • queue.buffering.max.ms

            默認值:5000,在 producer queue 的緩存的數(shù)據(jù)最大時間,僅僅 for asyc。

          • queue.buffering.max.message

            默認值:10000,producer 緩存的消息的最大數(shù)量,僅僅 for asyc。

          • queue.enqueue.timeout.ms

            默認值:-1,0 當 queue 滿時丟掉,負值是 queue 滿時 block, 正值是 queue 滿時 block 相應的時間,僅僅 for asyc。

          Kafka Consumer

          Kafka 有消費組的概念,每個消費者只能消費所分配到的分區(qū)的消息,每一個分區(qū)只能被一個消費組中的一個消費者所消費,所以同一個消費組中消費者的數(shù)量如果超過了分區(qū)的數(shù)量,將會出現(xiàn)有些消費者分配不到消費的分區(qū)。消費組與消費者關系如下圖所示:

          consumer group

          Kafka Consumer Client 消費消息通常包含以下步驟:

          1. 配置客戶端,創(chuàng)建消費者
          2. 訂閱主題
          3. 拉去消息并消費
          4. 提交消費位移
          5. 關閉消費者實例
          過程

          因為 Kafka 的 Consumer 客戶端是線程不安全的,為了保證線程安全,并提升消費性能,可以在 Consumer 端采用類似 Reactor 的線程模型來消費數(shù)據(jù)。

          消費模型

          Kafka consumer 參數(shù)

          • bootstrap.servers:連接 broker 地址,host:port 格式。
          • group.id:消費者隸屬的消費組。
          • key.deserializer:與生產(chǎn)者的key.serializer對應,key 的反序列化方式。
          • value.deserializer:與生產(chǎn)者的value.serializer對應,value 的反序列化方式。
          • session.timeout.ms:coordinator 檢測失敗的時間。默認 10s 該參數(shù)是 Consumer Group 主動檢測 (組內成員 comsummer) 崩潰的時間間隔,類似于心跳過期時間。
          • auto.offset.reset:該屬性指定了消費者在讀取一個沒有偏移量后者偏移量無效(消費者長時間失效當前的偏移量已經(jīng)過時并且被刪除了)的分區(qū)的情況下,應該作何處理,默認值是 latest,也就是從最新記錄讀取數(shù)據(jù)(消費者啟動之后生成的記錄),另一個值是 earliest,意思是在偏移量無效的情況下,消費者從起始位置開始讀取數(shù)據(jù)。
          • enable.auto.commit:否自動提交位移,如果為false,則需要在程序中手動提交位移。對于精確到一次的語義,最好手動提交位移
          • fetch.max.bytes:單次拉取數(shù)據(jù)的最大字節(jié)數(shù)量
          • max.poll.records:單次 poll 調用返回的最大消息數(shù),如果處理邏輯很輕量,可以適當提高該值。但是max.poll.records條數(shù)據(jù)需要在在 session.timeout.ms 這個時間內處理完 。默認值為 500
          • request.timeout.ms:一次請求響應的最長等待時間。如果在超時時間內未得到響應,kafka 要么重發(fā)這條消息,要么超過重試次數(shù)的情況下直接置為失敗。

          Kafka Rebalance

          rebalance 本質上是一種協(xié)議,規(guī)定了一個 consumer group 下的所有 consumer 如何達成一致來分配訂閱 topic 的每個分區(qū)。比如某個 group 下有 20 個 consumer,它訂閱了一個具有 100 個分區(qū)的 topic。正常情況下,Kafka 平均會為每個 consumer 分配 5 個分區(qū)。這個分配的過程就叫 rebalance。

          什么時候 rebalance?

          這也是經(jīng)常被提及的一個問題。rebalance 的觸發(fā)條件有三種:

          • 組成員發(fā)生變更(新 consumer 加入組、已有 consumer 主動離開組或已有 consumer 崩潰了——這兩者的區(qū)別后面會談到)
          • 訂閱主題數(shù)發(fā)生變更
          • 訂閱主題的分區(qū)數(shù)發(fā)生變更

          如何進行組內分區(qū)分配?

          Kafka 默認提供了兩種分配策略:Range 和 Round-Robin。當然 Kafka 采用了可插拔式的分配策略,你可以創(chuàng)建自己的分配器以實現(xiàn)不同的分配策略。

          答案關鍵字

          • Kafka 有哪些命令行工具?你用過哪些?/bin目錄,管理 kafka 集群、管理 topic、生產(chǎn)和消費 kafka
          • Kafka Producer 的執(zhí)行過程?攔截器,序列化器,分區(qū)器和累加器
          • Kafka Producer 有哪些常見配置?broker 配置,ack 配置,網(wǎng)絡和發(fā)送參數(shù),壓縮參數(shù),ack 參數(shù)
          • 如何讓 Kafka 的消息有序?Kafka 在 Topic 級別本身是無序的,只有 partition 上才有序,所以為了保證處理順序,可以自定義分區(qū)器,將需順序處理的數(shù)據(jù)發(fā)送到同一個 partition
          • Producer 如何保證數(shù)據(jù)發(fā)送不丟失?ack 機制,重試機制
          • 如何提升 Producer 的性能?批量,異步,壓縮
          • 如果同一 group 下 consumer 的數(shù)量大于 part 的數(shù)量,kafka 如何處理?多余的 Part 將處于無用狀態(tài),不消費數(shù)據(jù)
          • Kafka Consumer 是否是線程安全的?不安全,單線程消費,多線程處理
          • 講一下你使用 Kafka Consumer 消費消息時的線程模型,為何如此設計?拉取和處理分離
          • Kafka Consumer 的常見配置?broker, 網(wǎng)絡和拉取參數(shù),心跳參數(shù)
          • Consumer 什么時候會被踢出集群?奔潰,網(wǎng)絡異常,處理時間過長提交位移超時
          • 當有 Consumer 加入或退出時,Kafka 會作何反應?進行 Rebalance
          • 什么是 Rebalance,何時會發(fā)生 Rebalance?topic 變化,consumer 變化

          高可用和性能

          問題

          • Kafka 如何保證高可用?
          • Kafka 的交付語義?
          • Replic 的作用?
          • 什么事 AR,ISR?
          • Leader 和 Flower 是什么?
          • Kafka 中的 HW、LEO、LSO、LW 等分別代表什么?
          • Kafka 為保證優(yōu)越的性能做了哪些處理?

          分區(qū)與副本

          分區(qū)副本

          在分布式數(shù)據(jù)系統(tǒng)中,通常使用分區(qū)來提高系統(tǒng)的處理能力,通過副本來保證數(shù)據(jù)的高可用性。多分區(qū)意味著并發(fā)處理的能力,這多個副本中,只有一個是 leader,而其他的都是 follower 副本。僅有 leader 副本可以對外提供服務。多個 follower 副本通常存放在和 leader 副本不同的 broker 中。通過這樣的機制實現(xiàn)了高可用,當某臺機器掛掉后,其他 follower 副本也能迅速”轉正“,開始對外提供服務。

          為什么 follower 副本不提供讀服務?

          這個問題本質上是對性能和一致性的取舍。試想一下,如果 follower 副本也對外提供服務那會怎么樣呢?首先,性能是肯定會有所提升的。但同時,會出現(xiàn)一系列問題。類似數(shù)據(jù)庫事務中的幻讀,臟讀。比如你現(xiàn)在寫入一條數(shù)據(jù)到 kafka 主題 a,消費者 b 從主題 a 消費數(shù)據(jù),卻發(fā)現(xiàn)消費不到,因為消費者 b 去讀取的那個分區(qū)副本中,最新消息還沒寫入。而這個時候,另一個消費者 c 卻可以消費到最新那條數(shù)據(jù),因為它消費了 leader 副本。Kafka 通過 WH 和 Offset 的管理來決定 Consumer 可以消費哪些數(shù)據(jù),已經(jīng)當前寫入的數(shù)據(jù)。

          watermark

          只有 Leader 可以對外提供讀服務,那如何選舉 Leader

          kafka 會將與 leader 副本保持同步的副本放到 ISR 副本集合中。當然,leader 副本是一直存在于 ISR 副本集合中的,在某些特殊情況下,ISR 副本中甚至只有 leader 一個副本。當 leader 掛掉時,kakfa 通過 zookeeper 感知到這一情況,在 ISR 副本中選取新的副本成為 leader,對外提供服務。但這樣還有一個問題,前面提到過,有可能 ISR 副本集合中,只有 leader,當 leader 副本掛掉后,ISR 集合就為空,這時候怎么辦呢?這時候如果設置 unclean.leader.election.enable 參數(shù)為 true,那么 kafka 會在非同步,也就是不在 ISR 副本集合中的副本中,選取出副本成為 leader。

          副本的存在就會出現(xiàn)副本同步問題

          Kafka 在所有分配的副本 (AR) 中維護一個可用的副本列表 (ISR),Producer 向 Broker 發(fā)送消息時會根據(jù)ack配置來確定需要等待幾個副本已經(jīng)同步了消息才相應成功,Broker 內部會ReplicaManager服務來管理 flower 與 leader 之間的數(shù)據(jù)同步。

          sync

          性能優(yōu)化

          • partition 并發(fā)
          • 順序讀寫磁盤
          • page cache:按頁讀寫
          • 預讀:Kafka 會將將要消費的消息提前讀入內存
          • 高性能序列化(二進制)
          • 內存映射
          • 無鎖 offset 管理:提高并發(fā)能力
          • Java NIO 模型
          • 批量:批量讀寫
          • 壓縮:消息壓縮,存儲壓縮,減小網(wǎng)絡和 IO 開銷

          Partition 并發(fā)

          一方面,由于不同 Partition 可位于不同機器,因此可以充分利用集群優(yōu)勢,實現(xiàn)機器間的并行處理。另一方面,由于 Partition 在物理上對應一個文件夾,即使多個 Partition 位于同一個節(jié)點,也可通過配置讓同一節(jié)點上的不同 Partition 置于不同的 disk drive 上,從而實現(xiàn)磁盤間的并行處理,充分發(fā)揮多磁盤的優(yōu)勢。

          順序讀寫

          Kafka 每一個 partition 目錄下的文件被平均切割成大小相等(默認一個文件是 500 兆,可以手動去設置)的數(shù)據(jù)文件, 每一個數(shù)據(jù)文件都被稱為一個段(segment file), 每個 segment 都采用 append 的方式追加數(shù)據(jù)。

          追加數(shù)據(jù)

          答案關鍵字

          • Kafka 如何保證高可用?

            通過副本來保證數(shù)據(jù)的高可用,producer ack、重試、自動 Leader 選舉,Consumer 自平衡

          • Kafka 的交付語義?

            交付語義一般有at least onceat most onceexactly once。kafka 通過 ack 的配置來實現(xiàn)前兩種。

          • Replic 的作用?

            實現(xiàn)數(shù)據(jù)的高可用

          • 什么是 AR,ISR?

            AR:Assigned Replicas。AR 是主題被創(chuàng)建后,分區(qū)創(chuàng)建時被分配的副本集合,副本個 數(shù)由副本因子決定。ISR:In-Sync Replicas。Kafka 中特別重要的概念,指代的是 AR 中那些與 Leader 保 持同步的副本集合。在 AR 中的副本可能不在 ISR 中,但 Leader 副本天然就包含在 ISR 中。關于 ISR,還有一個常見的面試題目是如何判斷副本是否應該屬于 ISR。目前的判斷 依據(jù)是:Follower 副本的 LEO 落后 Leader LEO 的時間,是否超過了 Broker 端參數(shù) replica.lag.time.max.ms 值。如果超過了,副本就會被從 ISR 中移除。

          • Leader 和 Flower 是什么?

          • Kafka 中的 HW 代表什么?

            高水位值 (High watermark)。這是控制消費者可讀取消息范圍的重要字段。一 個普通消費者只能“看到”Leader 副本上介于 Log Start Offset 和 HW(不含)之間的 所有消息。水位以上的消息是對消費者不可見的。

          • Kafka 為保證優(yōu)越的性能做了哪些處理?

            partition 并發(fā)、順序讀寫磁盤、page cache 壓縮、高性能序列化(二進制)、內存映射 無鎖 offset 管理、Java NIO 模型

          建議讀者同學結合 Kafka 的配置去了解 Kafka 的實現(xiàn)原理,Kafka 有大量的配置,這也是 Kafka 高度擴展的一個表現(xiàn),很多同學對 Kafka 的配置也不敢輕易改動。所以理解這些配置背后的實現(xiàn)原理,可以讓我們在實踐中懂得如何使用和優(yōu)化 Kafka。既可面試造火箭,也可以實戰(zhàn)造火箭。

          Kafka 配置說明鏈接:https://kafka.apache.org/documentation


          Architecture

          理解 Kafka 架構,就是理解 Kafka 的各種組件的概念,以及這些組件的關系。先簡單看一下各組件及其簡單說明。

          不要去嘗試記憶他們

          Producer: 生產(chǎn)者,發(fā)送消息的一方。生產(chǎn)者負責創(chuàng)建消息,然后將其發(fā)送到 Kafka。
          Consumer: 消費者,接受消息的一方。消費者連接到 Kafka 上并接收消息,進而進行相應的業(yè)務邏輯處理。
          Consumer Group: 一個消費者組可以包含一個或多個消費者。使用多分區(qū) + 多消費者方式可以極大提高數(shù)據(jù)下游的處理速度,同一消費組中的消費者不會重復消費消息,同樣的,不同消費組中的消費者消息消息時互不影響。Kafka 就是通過消費組的方式來實現(xiàn)消息 P2P 模式和廣播模式。
          Broker: 服務代理節(jié)點。Broker 是 Kafka 的服務節(jié)點,即 Kafka 的服務器。
          Topic: Kafka 中的消息以 Topic 為單位進行劃分,生產(chǎn)者將消息發(fā)送到特定的 Topic,而消費者負責訂閱 Topic 的消息并進行消費。
          Partition: Topic 是一個邏輯的概念,它可以細分為多個分區(qū),每個分區(qū)只屬于單個主題。同一個主題下不同分區(qū)包含的消息是不同的,分區(qū)在存儲層面可以看作一個可追加的日志(Log)文件,消息在被追加到分區(qū)日志文件的時候都會分配一個特定的偏移量(offset)。
          Offset: offset 是消息在分區(qū)中的唯一標識,Kafka 通過它來保證消息在分區(qū)內的順序性,不過 offset 并不跨越分區(qū),也就是說,Kafka 保證的是分區(qū)有序性而不是主題有序性。
          Replication: 副本,是 Kafka 保證數(shù)據(jù)高可用的方式,Kafka 同一 Partition 的數(shù)據(jù)可以在多 Broker 上存在多個副本,通常只有主副本對外提供讀寫服務,當主副本所在 broker 崩潰或發(fā)生網(wǎng)絡異常,Kafka 會在 Controller 的管理下會重新選擇新的 Leader 副本對外提供讀寫服務。
          Record: 實際寫入 Kafka 中并可以被讀取的消息記錄。每個 record 包含了 key、value 和 timestamp。

          我們理解了也就自然記住了

          我們應該通過理解的方式去記憶它們。

          生產(chǎn)者-消費者

          生產(chǎn)者-消費者是一種設計模式,生產(chǎn)者消費者之間通過添加一個中間組件來達到解耦。生產(chǎn)者中間組件生成數(shù)據(jù),消費者消費數(shù)據(jù)。

          就像讀書時65 哥給小芳寫情書,這里寫情書就是生產(chǎn)者,情書就是消息,小芳就是消費者。但有時候小芳不在,或者比較忙,65 哥也比較害羞,不敢直接將情書塞小芳手里,于是將情書塞在小芳抽屜中。所以抽屜就是這個中間組件

          在程序中我們通常使用Queue來作為這個中間組件。可以使用多線程向隊列中寫入數(shù)據(jù),另外的消費者線程依次讀取隊列中的數(shù)據(jù)進行消費。模型如下圖所示:

          生產(chǎn)者-消費者模式通過添加一個中間層,不僅可以解耦生產(chǎn)者和消費者,使其易于擴展,還可以異步化調用、緩沖消息等。

          分布式隊列

          后來 65 哥和小芳異地了,65 哥在卷都奮斗,小芳在魔都逛街。于是只能通過郵局寄曖昧信了。這樣 65 哥、郵局和小芳就成了分布式的了。65 哥將信件發(fā)給郵局,小芳從郵局拿到 65 哥寫的信,再回去慢慢看。

          Kafka 的消息生產(chǎn)者就是Producer,上游消費者進程添加 Kafka Client 創(chuàng)建 Kafka Producer,向 Broker 發(fā)送消息,Broker 是集群部署在遠程服務器上的 Kafka Server 進程,下游消費者進程引入 Kafka Consumer API 持續(xù)消費隊列中消息。

          因為 Kafka Consumer 使用 Poll 的模式,需要 Consumer 主動拉去消息。所有小芳只能定期去郵局拿信件了(呃,果然主動權都在小芳手上啊)。

          主題

          郵局不能只為 65 哥服務,雖然 65 哥一天寫好幾封信。但也無法挽回郵局的損失。所以郵局是可以供任何人寄信。只需要寄信人寫好地址(主題),郵局建有兩地的通道就可以發(fā)收信件了。

          Kafka 的 Topic 才相當于一個隊列,Broker 是所有隊列部署的機器。可以按業(yè)務創(chuàng)建不同的 Topic,Producer 向所屬業(yè)務的 Topic 發(fā)送消息,相應的 Consumer 可以消費并處理消息。

          分區(qū)

          由于 65 哥寫的信太多,一個郵局已經(jīng)無法滿足 65 哥的需求,郵政公司只能多建幾個郵局了,65 哥將信件按私密度分類(分區(qū)策略),從不同的郵局寄送。

          同一個 Topic 可以創(chuàng)建多個分區(qū)。理論上分區(qū)越多并發(fā)度越高,Kafka 會根據(jù)分區(qū)策略將分區(qū)盡可能均衡的分布在不同的 Broker 節(jié)點上,以避免消息傾斜,不同的 Broker 負載差異太大。分區(qū)也不是越多越好哦,畢竟太多郵政公司也管理不過來。

          副本

          為防止由于郵局的問題,比如交通斷啦,郵車沒油啦。導致 65 哥的曖昧信無法寄到小芳手上,使得 65 哥晚上遠程跪鍵盤。郵局決定將 65 哥的信件復制幾份發(fā)到多個正常的郵局,這樣只要有一個郵局還在,小芳就可以收到 65 哥的信了。

          Kafka 采用分區(qū)副本的方式來保證數(shù)據(jù)的高可用,每個分區(qū)都將建立指定數(shù)量的副本數(shù),kakfa 保證同一分區(qū)副本盡量分布在不同的 Broker 節(jié)點上,以防止 Broker 宕機導致所有副本不可用。Kafka 會為分區(qū)的多個副本選舉一個作為主副本(Leader),主副本對外提供讀寫服務,從副本(Follower)實時同步 Leader 的數(shù)據(jù)。

          多消費者

          哎,65 哥的信件滿天飛,小芳天天跑郵局,還要一一拆開看,65 哥寫的信又臭又長,讓小芳忙得滿身大漢大汗。于是小芳啪的一下,很快啊,變出多個分身去不同的郵局取信,這樣小芳終于可以擠出額外的時間逛街了。

          廣播消息

          郵局最近提供了定制明信片業(yè)務,每個人都可以設計明信片,同一個身份只能領取一種明信片。65 哥設計了一堆,廣播給所有漂亮的小妹妹都可以來領取,美女啪變出的分身也可以來領取,但是同一個身份的多個分身只能取一種明信片。

          Kafka 通過 Consumer Group 來實現(xiàn)廣播模式消息訂閱,即不同 group 下的 consumer 可以重復消費消息,相互不影響,同一個 group 下的 consumer 構成一個整體。

          最后我們完成了 Kafka 的整體架構,如下:

          Zookeeper

          Zookeeper 是一個成熟的分布式協(xié)調服務,它可以為分布式服務提供分布式配置服、同步服務和命名注冊等能力.。對于任何分布式系統(tǒng),都需要一種協(xié)調任務的方法。Kafka 是使用 ZooKeeper 而構建的分布式系統(tǒng)。但是也有一些其他技術(例如 Elasticsearch 和 MongoDB)具有其自己的內置任務協(xié)調機制。

          Kafka 將 Broker、Topic 和 Partition 的元數(shù)據(jù)信息存儲在 Zookeeper 上。通過在 Zookeeper 上建立相應的數(shù)據(jù)節(jié)點,并監(jiān)聽節(jié)點的變化,Kafka 使用 Zookeeper 完成以下功能:

          • Kafka Controller 的 Leader 選舉
          • Kafka 集群成員管理
          • Topic 配置管理
          • 分區(qū)副本管理

          我們看一看 Zookeeper 下 Kafka 創(chuàng)建的節(jié)點,即可一目了然的看出這些相關的功能。

          Controller

          Controller 是從 Broker 中選舉出來的,負責分區(qū) Leader 和 Follower 的管理。當某個分區(qū)的 leader 副本發(fā)生故障時,由 Controller 負責為該分區(qū)選舉新的 leader 副本。當檢測到某個分區(qū)的 ISR(In-Sync Replica)集合發(fā)生變化時,由控制器負責通知所有 broker 更新其元數(shù)據(jù)信息。當使用kafka-topics.sh腳本為某個 topic 增加分區(qū)數(shù)量時,同樣還是由控制器負責分區(qū)的重新分配。

          Kafka 中 Contorller 的選舉的工作依賴于 Zookeeper,成功競選為控制器的 broker 會在 Zookeeper 中創(chuàng)建/controller這個臨時(EPHEMERAL)節(jié)點。

          選舉過程

          Broker 啟動的時候嘗試去讀取/controller節(jié)點的brokerid的值,如果brokerid的值不等于-1,則表明已經(jīng)有其他的 Broker 成功成為 Controller 節(jié)點,當前 Broker 主動放棄競選;如果不存在/controller節(jié)點,或者 brokerid 數(shù)值異常,當前 Broker 嘗試去創(chuàng)建/controller這個節(jié)點,此時也有可能其他 broker 同時去嘗試創(chuàng)建這個節(jié)點,只有創(chuàng)建成功的那個 broker 才會成為控制器,而創(chuàng)建失敗的 broker 則表示競選失敗。每個 broker 都會在內存中保存當前控制器的 brokerid 值,這個值可以標識為 activeControllerId。

          實現(xiàn)

          Controller 讀取 Zookeeper 中的節(jié)點數(shù)據(jù),初始化上下文(Controller Context),并管理節(jié)點變化,變更上下文,同時也需要將這些變更信息同步到其他普通的 broker 節(jié)點中。Controller 通過定時任務,或者監(jiān)聽器模式獲取 zookeeper 信息,事件監(jiān)聽會更新更新上下文信息,如圖所示,Controller 內部也采用生產(chǎn)者-消費者實現(xiàn)模式,Controller 將 zookeeper 的變動通過事件的方式發(fā)送給事件隊列,隊列就是一個LinkedBlockingQueue,事件消費者線程組通過消費消費事件,將相應的事件同步到各 Broker 節(jié)點。這種隊列 FIFO 的模式保證了消息的有序性。

          職責

          Controller 被選舉出來,作為整個 Broker 集群的管理者,管理所有的集群信息和元數(shù)據(jù)信息。它的職責包括下面幾部分:

          1. 處理 Broker 節(jié)點的上線和下線,包括自然下線、宕機和網(wǎng)絡不可達導致的集群變動,Controller 需要及時更新集群元數(shù)據(jù),并將集群變化通知到所有的 Broker 集群節(jié)點;
          2. 創(chuàng)建 Topic 或者 Topic 擴容分區(qū),Controller 需要負責分區(qū)副本的分配工作,并主導 Topic 分區(qū)副本的 Leader 選舉。
          3. 管理集群中所有的副本和分區(qū)的狀態(tài)機,監(jiān)聽狀態(tài)機變化事件,并作出相應的處理。Kafka 分區(qū)和副本數(shù)據(jù)采用狀態(tài)機的方式管理,分區(qū)和副本的變化都在狀態(tài)機內會引起狀態(tài)機狀態(tài)的變更,從而觸發(fā)相應的變化事件。

          狀態(tài)機啊,聽起來好復雜。

          Controller 管理著集群中所有副本和分區(qū)的狀態(tài)機。大家不要被狀態(tài)機這個詞唬住了。理解狀態(tài)機很簡單。先理解模型,即這是什么關于什么模型,然后就是模型的狀態(tài)有哪些,模型狀態(tài)之間如何轉換,轉換時發(fā)送相應的變化事件。

          Kafka 的分區(qū)和副本狀態(tài)機很簡單。我們先理解,這分別是管理 Kafka Topic 的分區(qū)和副本的。它們的狀態(tài)也很簡單,就是 CRUD,具體說來如下:

          分區(qū)狀態(tài)機

          PartitionStateChange,管理 Topic 的分區(qū),它有以下 4 種狀態(tài):

          1. NonExistentPartition:該狀態(tài)表示分區(qū)沒有被創(chuàng)建過或創(chuàng)建后被刪除了。
          2. NewPartition:分區(qū)剛創(chuàng)建后,處于這個狀態(tài)。此狀態(tài)下分區(qū)已經(jīng)分配了副本,但是還沒有選舉 leader,也沒有 ISR 列表。
          3. OnlinePartition:一旦這個分區(qū)的 leader 被選舉出來,將處于這個狀態(tài)。
          4. OfflinePartition:當分區(qū)的 leader 宕機,轉移到這個狀態(tài)。

          我們用一張圖來直觀的看看這些狀態(tài)是如何變化的,以及在狀態(tài)發(fā)生變化時 Controller 都有哪些操作:

          副本狀態(tài)機

          ReplicaStateChange,副本狀態(tài),管理分區(qū)副本信息,它也有 4 種狀態(tài):

          1. NewReplica: 創(chuàng)建 topic 和分區(qū)分配后創(chuàng)建 replicas,此時,replica 只能獲取到成為 follower 狀態(tài)變化請求。
          2. OnlineReplica: 當 replica 成為 parition 的 assingned replicas 時,其狀態(tài)變?yōu)?OnlineReplica, 即一個有效的 OnlineReplica。
          3. OfflineReplica: 當一個 replica 下線,進入此狀態(tài),這一般發(fā)生在 broker 宕機的情況下;
          4. NonExistentReplica: Replica 成功刪除后,replica 進入 NonExistentReplica 狀態(tài)。

          副本狀態(tài)間的變化如下圖所示,Controller 在狀態(tài)變化時會做出相應的操作:

          Network

          Kafka 的網(wǎng)絡通信模型是基于 NIO 的 Reactor 多線程模型來設計的。其中包含了一個Acceptor線程,用于處理新的連接,Acceptor 有 N 個 Processor 線程 select 和 read socket 請求,N 個 Handler 線程處理請求并相應,即處理業(yè)務邏輯。下面就是 KafkaServer 的模型圖:


          Kafka 性能全景

          從高度抽象的角度來看,性能問題逃不出下面三個方面:

          • 網(wǎng)絡
          • 磁盤
          • 復雜度

          對于 Kafka 這種網(wǎng)絡分布式隊列來說,網(wǎng)絡和磁盤更是優(yōu)化的重中之重。針對于上面提出的抽象問題,解決方案高度抽象出來也很簡單:

          • 并發(fā)
          • 壓縮
          • 批量
          • 緩存
          • 算法

          知道了問題和思路,我們再來看看,在 Kafka 中,有哪些角色,而這些角色就是可以優(yōu)化的點:

          • Producer
          • Broker
          • Consumer

          是的,所有的問題,思路,優(yōu)化點都已經(jīng)列出來了,我們可以盡可能的細化,三個方向都可以細化,如此,所有的實現(xiàn)便一目了然,即使不看 Kafka 的實現(xiàn),我們自己也可以想到一二點可以優(yōu)化的地方。

          這就是思考方式。提出問題 > 列出問題點 > 列出優(yōu)化方法 > 列出具體可切入的點 > tradeoff和細化實現(xiàn)

          現(xiàn)在,你也可以嘗試自己想一想優(yōu)化的點和方法,不用盡善盡美,不用管好不好實現(xiàn),想一點是一點。

          不行啊,我很笨,也很懶,你還是直接和我說吧,我白嫖比較行。

          順序寫

          人家 Redis 是基于純內存的系統(tǒng),你 kafka 還要讀寫磁盤,能比?

          為什么說寫磁盤慢?

          我們不能只知道結論,而不知其所以然。要回答這個問題,就得回到在校時我們學的操作系統(tǒng)課程了。來,翻到講磁盤的章節(jié),讓我們回顧一下磁盤的運行原理。

          鬼還留著哦,課程還沒上到一半書就沒了。要不是考試俺眼神好,估計現(xiàn)在還沒畢業(yè)。

          看經(jīng)典大圖:

          完成一次磁盤 IO,需要經(jīng)過尋道旋轉數(shù)據(jù)傳輸三個步驟。

          影響磁盤 IO 性能的因素也就發(fā)生在上面三個步驟上,因此主要花費的時間就是:

          1. 尋道時間:Tseek 是指將讀寫磁頭移動至正確的磁道上所需要的時間。尋道時間越短,I/O 操作越快,目前磁盤的平均尋道時間一般在 3-15ms。
          2. 旋轉延遲:Trotation 是指盤片旋轉將請求數(shù)據(jù)所在的扇區(qū)移動到讀寫磁盤下方所需要的時間。旋轉延遲取決于磁盤轉速,通常用磁盤旋轉一周所需時間的 1/2 表示。比如:7200rpm 的磁盤平均旋轉延遲大約為 60*1000/7200/2 = 4.17ms,而轉速為 15000rpm 的磁盤其平均旋轉延遲為 2ms。
          3. 數(shù)據(jù)傳輸時間:Ttransfer 是指完成傳輸所請求的數(shù)據(jù)所需要的時間,它取決于數(shù)據(jù)傳輸率,其值等于數(shù)據(jù)大小除以數(shù)據(jù)傳輸率。目前 IDE/ATA 能達到 133MB/s,SATA II 可達到 300MB/s 的接口數(shù)據(jù)傳輸率,數(shù)據(jù)傳輸時間通常遠小于前兩部分消耗時間。簡單計算時可忽略。

          因此,如果在寫磁盤的時候省去尋道旋轉可以極大地提高磁盤讀寫的性能。

          Kafka 采用順序寫文件的方式來提高磁盤寫入性能。順序寫文件,基本減少了磁盤尋道旋轉的次數(shù)。磁頭再也不用在磁道上亂舞了,而是一路向前飛速前行。

          Kafka 中每個分區(qū)是一個有序的,不可變的消息序列,新的消息不斷追加到 Partition 的末尾,在 Kafka 中 Partition 只是一個邏輯概念,Kafka 將 Partition 劃分為多個 Segment,每個 Segment 對應一個物理文件,Kafka 對 segment 文件追加寫,這就是順序寫文件。

          為什么 Kafka 可以使用追加寫的方式呢?

          這和 Kafka 的性質有關,我們來看看 Kafka 和 Redis,說白了,Kafka 就是一個Queue,而 Redis 就是一個HashMapQueueMap的區(qū)別是什么?

          Queue 是 FIFO 的,數(shù)據(jù)是有序的;HashMap數(shù)據(jù)是無序的,是隨機讀寫的。Kafka 的不可變性,有序性使得 Kafka 可以使用追加寫的方式寫文件。

          其實很多符合以上特性的數(shù)據(jù)系統(tǒng),都可以采用追加寫的方式來優(yōu)化磁盤性能。典型的有Redis的 AOF 文件,各種數(shù)據(jù)庫的WAL(Write ahead log)機制等等。

          所以清楚明白自身業(yè)務的特點,就可以針對性地做出優(yōu)化。

          零拷貝

          哈哈,這個我面試被問到過。可惜答得一般般,唉。

          什么是零拷貝?

          我們從 Kafka 的場景來看,Kafka Consumer 消費存儲在 Broker 磁盤的數(shù)據(jù),從讀取 Broker 磁盤到網(wǎng)絡傳輸給 Consumer,期間涉及哪些系統(tǒng)交互。Kafka Consumer 從 Broker 消費數(shù)據(jù),Broker 讀取 Log,就使用了 sendfile。如果使用傳統(tǒng)的 IO 模型,偽代碼邏輯就如下所示:

          readFile(buffer)
          send(buffer)

          如圖,如果采用傳統(tǒng)的 IO 流程,先讀取網(wǎng)絡 IO,再寫入磁盤 IO,實際需要將數(shù)據(jù) Copy 四次。

          1. 第一次:讀取磁盤文件到操作系統(tǒng)內核緩沖區(qū);
          2. 第二次:將內核緩沖區(qū)的數(shù)據(jù),copy 到應用程序的 buffer;
          3. 第三步:將應用程序 buffer 中的數(shù)據(jù),copy 到 socket 網(wǎng)絡發(fā)送緩沖區(qū);
          4. 第四次:將 socket buffer 的數(shù)據(jù),copy 到網(wǎng)卡,由網(wǎng)卡進行網(wǎng)絡傳輸。

          啊,操作系統(tǒng)這么傻嗎?copy 來 copy 去的。

          并不是操作系統(tǒng)傻,操作系統(tǒng)的設計就是每個應用程序都有自己的用戶內存,用戶內存和內核內存隔離,這是為了程序和系統(tǒng)安全考慮,否則的話每個應用程序內存滿天飛,隨意讀寫那還得了。

          不過,還有零拷貝技術,英文——Zero-Copy零拷貝就是盡量去減少上面數(shù)據(jù)的拷貝次數(shù),從而減少拷貝的 CPU 開銷,減少用戶態(tài)內核態(tài)的上下文切換次數(shù),從而優(yōu)化數(shù)據(jù)傳輸?shù)男阅堋?/p>

          常見的零拷貝思路主要有三種:

          • 直接 I/O:數(shù)據(jù)直接跨過內核,在用戶地址空間與 I/O 設備之間傳遞,內核只是進行必要的虛擬存儲配置等輔助工作;
          • 避免內核和用戶空間之間的數(shù)據(jù)拷貝:當應用程序不需要對數(shù)據(jù)進行訪問時,則可以避免將數(shù)據(jù)從內核空間拷貝到用戶空間;
          • 寫時復制:數(shù)據(jù)不需要提前拷貝,而是當需要修改的時候再進行部分拷貝。

          Kafka 使用到了 mmap 和 sendfile 的方式來實現(xiàn)零拷貝。分別對應 Java 的 MappedByteBuffer 和 FileChannel.transferTo

          使用 Java NIO 實現(xiàn)零拷貝,如下:

          FileChannel.transferTo()

          在此模型下,上下文切換的數(shù)量減少到一個。具體而言,transferTo()方法指示塊設備通過 DMA 引擎將數(shù)據(jù)讀取到讀取緩沖區(qū)中。然后,將該緩沖區(qū)復制到另一個內核緩沖區(qū)以暫存到套接字。最后,套接字緩沖區(qū)通過 DMA 復制到 NIC 緩沖區(qū)。

          我們將副本數(shù)從四減少到三,并且這些副本中只有一個涉及 CPU。我們還將上下文切換的數(shù)量從四個減少到了兩個。這是一個很大的改進,但是還沒有查詢零副本。當運行 Linux 內核 2.4 及更高版本以及支持收集操作的網(wǎng)絡接口卡時,后者可以作為進一步的優(yōu)化來實現(xiàn)。如下所示。

          根據(jù)前面的示例,調用transferTo()方法會使設備通過 DMA 引擎將數(shù)據(jù)讀取到內核讀取緩沖區(qū)中。但是,使用gather操作時,讀取緩沖區(qū)和套接字緩沖區(qū)之間沒有復制。取而代之的是,給 NIC 一個指向讀取緩沖區(qū)的指針以及偏移量和長度,該偏移量和長度由 DMA 清除。CPU 絕對不參與復制緩沖區(qū)。

          關于零拷貝詳情,可以詳讀這篇文章零拷貝 (Zero-copy) 淺析及其應用。

          PageCache

          producer 生產(chǎn)消息到 Broker 時,Broker 會使用 pwrite() 系統(tǒng)調用【對應到 Java NIO 的 FileChannel.write() API】按偏移量寫入數(shù)據(jù),此時數(shù)據(jù)都會先寫入page cache。consumer 消費消息時,Broker 使用 sendfile() 系統(tǒng)調用【對應 FileChannel.transferTo() API】,零拷貝地將數(shù)據(jù)從 page cache 傳輸?shù)?broker 的 Socket buffer,再通過網(wǎng)絡傳輸。

          leader 與 follower 之間的同步,與上面 consumer 消費數(shù)據(jù)的過程是同理的。

          page cache中的數(shù)據(jù)會隨著內核中 flusher 線程的調度以及對 sync()/fsync() 的調用寫回到磁盤,就算進程崩潰,也不用擔心數(shù)據(jù)丟失。另外,如果 consumer 要消費的消息不在page cache里,才會去磁盤讀取,并且會順便預讀出一些相鄰的塊放入 page cache,以方便下一次讀取。

          因此如果 Kafka producer 的生產(chǎn)速率與 consumer 的消費速率相差不大,那么就能幾乎只靠對 broker page cache 的讀寫完成整個生產(chǎn) - 消費過程,磁盤訪問非常少。

          網(wǎng)絡模型

          網(wǎng)絡嘛,作為 Java 程序員,自然是 Netty

          是的,Netty 是 JVM 領域一個優(yōu)秀的網(wǎng)絡框架,提供了高性能的網(wǎng)絡服務。大多數(shù) Java 程序員提到網(wǎng)絡框架,首先想到的就是 Netty。Dubbo、Avro-RPC 等等優(yōu)秀的框架都使用 Netty 作為底層的網(wǎng)絡通信框架。

          Kafka 自己實現(xiàn)了網(wǎng)絡模型做 RPC。底層基于 Java NIO,采用和 Netty 一樣的 Reactor 線程模型。

          Reacotr 模型主要分為三個角色

          • Reactor:把 IO 事件分配給對應的 handler 處理
          • Acceptor:處理客戶端連接事件
          • Handler:處理非阻塞的任務

          在傳統(tǒng)阻塞 IO 模型中,每個連接都需要獨立線程處理,當并發(fā)數(shù)大時,創(chuàng)建線程數(shù)多,占用資源;采用阻塞 IO 模型,連接建立后,若當前線程沒有數(shù)據(jù)可讀,線程會阻塞在讀操作上,造成資源浪費

          針對傳統(tǒng)阻塞 IO 模型的兩個問題,Reactor 模型基于池化思想,避免為每個連接創(chuàng)建線程,連接完成后將業(yè)務處理交給線程池處理;基于 IO 復用模型,多個連接共用同一個阻塞對象,不用等待所有的連接。遍歷到有新數(shù)據(jù)可以處理時,操作系統(tǒng)會通知程序,線程跳出阻塞狀態(tài),進行業(yè)務邏輯處理

          Kafka 即基于 Reactor 模型實現(xiàn)了多路復用和處理線程池。其設計如下:

          其中包含了一個Acceptor線程,用于處理新的連接,Acceptor 有 N 個 Processor 線程 select 和 read socket 請求,N 個 Handler 線程處理請求并相應,即處理業(yè)務邏輯。

          I/O 多路復用可以通過把多個 I/O 的阻塞復用到同一個 select 的阻塞上,從而使得系統(tǒng)在單線程的情況下可以同時處理多個客戶端請求。它的最大優(yōu)勢是系統(tǒng)開銷小,并且不需要創(chuàng)建新的進程或者線程,降低了系統(tǒng)的資源開銷。

          總結: Kafka Broker 的 KafkaServer 設計是一個優(yōu)秀的網(wǎng)絡架構,有想了解 Java 網(wǎng)絡編程,或需要使用到這方面技術的同學不妨去讀一讀源碼。

          批量與壓縮

          Kafka Producer 向 Broker 發(fā)送消息不是一條消息一條消息的發(fā)送。使用過 Kafka 的同學應該知道,Producer 有兩個重要的參數(shù):batch.sizelinger.ms。這兩個參數(shù)就和 Producer 的批量發(fā)送有關。

          Kafka Producer 的執(zhí)行流程如下圖所示:

          發(fā)送消息依次經(jīng)過以下處理器:

          • Serialize:鍵和值都根據(jù)傳遞的序列化器進行序列化。優(yōu)秀的序列化方式可以提高網(wǎng)絡傳輸?shù)男省?/section>
          • Partition:決定將消息寫入主題的哪個分區(qū),默認情況下遵循 murmur2 算法。自定義分區(qū)程序也可以傳遞給生產(chǎn)者,以控制應將消息寫入哪個分區(qū)。
          • Compress:默認情況下,在 Kafka 生產(chǎn)者中不啟用壓縮.Compression 不僅可以更快地從生產(chǎn)者傳輸?shù)酱恚€可以在復制過程中進行更快的傳輸。壓縮有助于提高吞吐量,降低延遲并提高磁盤利用率。
          • Accumulate:Accumulate顧名思義,就是一個消息累計器。其內部為每個 Partition 維護一個Deque雙端隊列,隊列保存將要發(fā)送的批次數(shù)據(jù),Accumulate將數(shù)據(jù)累計到一定數(shù)量,或者在一定過期時間內,便將數(shù)據(jù)以批次的方式發(fā)送出去。記錄被累積在主題每個分區(qū)的緩沖區(qū)中。根據(jù)生產(chǎn)者批次大小屬性將記錄分組。主題中的每個分區(qū)都有一個單獨的累加器 / 緩沖區(qū)。
          • Group Send:記錄累積器中分區(qū)的批次按將它們發(fā)送到的代理分組。批處理中的記錄基于 batch.size 和 linger.ms 屬性發(fā)送到代理。記錄由生產(chǎn)者根據(jù)兩個條件發(fā)送。當達到定義的批次大小或達到定義的延遲時間時。

          Kafka 支持多種壓縮算法:lz4、snappy、gzip。Kafka 2.1.0 正式支持 ZStandard —— ZStandard 是 Facebook 開源的壓縮算法,旨在提供超高的壓縮比 (compression ratio),具體細節(jié)參見 zstd。

          Producer、Broker 和 Consumer 使用相同的壓縮算法,在 producer 向 Broker 寫入數(shù)據(jù),Consumer 向 Broker 讀取數(shù)據(jù)時甚至可以不用解壓縮,最終在 Consumer Poll 到消息時才解壓,這樣節(jié)省了大量的網(wǎng)絡和磁盤開銷。

          分區(qū)并發(fā)

          Kafka 的 Topic 可以分成多個 Partition,每個 Paritition 類似于一個隊列,保證數(shù)據(jù)有序。同一個 Group 下的不同 Consumer 并發(fā)消費 Paritition,分區(qū)實際上是調優(yōu) Kafka 并行度的最小單元,因此,可以說,每增加一個 Paritition 就增加了一個消費并發(fā)。

          Kafka 具有優(yōu)秀的分區(qū)分配算法——StickyAssignor,可以保證分區(qū)的分配盡量地均衡,且每一次重分配的結果盡量與上一次分配結果保持一致。這樣,整個集群的分區(qū)盡量地均衡,各個 Broker 和 Consumer 的處理不至于出現(xiàn)太大的傾斜。

          那是不是分區(qū)數(shù)越多越好呢?

          當然不是。

          越多的分區(qū)需要打開更多的文件句柄

          在 kafka 的 broker 中,每個分區(qū)都會對照著文件系統(tǒng)的一個目錄。在 kafka 的數(shù)據(jù)日志文件目錄中,每個日志數(shù)據(jù)段都會分配兩個文件,一個索引文件和一個數(shù)據(jù)文件。因此,隨著 partition 的增多,需要的文件句柄數(shù)急劇增加,必要時需要調整操作系統(tǒng)允許打開的文件句柄數(shù)。

          客戶端 / 服務器端需要使用的內存就越多

          客戶端 producer 有個參數(shù) batch.size,默認是 16KB。它會為每個分區(qū)緩存消息,一旦滿了就打包將消息批量發(fā)出。看上去這是個能夠提升性能的設計。不過很顯然,因為這個參數(shù)是分區(qū)級別的,如果分區(qū)數(shù)越多,這部分緩存所需的內存占用也會更多。

          降低高可用性

          分區(qū)越多,每個 Broker 上分配的分區(qū)也就越多,當一個發(fā)生 Broker 宕機,那么恢復時間將很長。

          文件結構

          Kafka 消息是以 Topic 為單位進行歸類,各個 Topic 之間是彼此獨立的,互不影響。每個 Topic 又可以分為一個或多個分區(qū)。每個分區(qū)各自存在一個記錄消息數(shù)據(jù)的日志文件。

          Kafka 每個分區(qū)日志在物理上實際按大小被分成多個 Segment。

          • segment file 組成:由 2 大部分組成,分別為 index file 和 data file,此 2 個文件一一對應,成對出現(xiàn),后綴”.index”和“.log”分別表示為 segment 索引文件、數(shù)據(jù)文件。
          • segment 文件命名規(guī)則:partion 全局的第一個 segment 從 0 開始,后續(xù)每個 segment 文件名為上一個 segment 文件最后一條消息的 offset 值。數(shù)值最大為 64 位 long 大小,19 位數(shù)字字符長度,沒有數(shù)字用 0 填充。

          index 采用稀疏索引,這樣每個 index 文件大小有限,Kafka 采用mmap的方式,直接將 index 文件映射到內存,這樣對 index 的操作就不需要操作磁盤 IO。mmap的 Java 實現(xiàn)對應 MappedByteBuffer 。

          mmap 是一種內存映射文件的方法。即將一個文件或者其它對象映射到進程的地址空間,實現(xiàn)文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關系。實現(xiàn)這樣的映射關系后,進程就可以采用指針的方式讀寫操作這一段內存,而系統(tǒng)會自動回寫臟頁面到對應的文件磁盤上,即完成了對文件的操作而不必再調用 read,write 等系統(tǒng)調用函數(shù)。相反,內核空間對這段區(qū)域的修改也直接反映用戶空間,從而可以實現(xiàn)不同進程間的文件共享。

          Kafka 充分利用二分法來查找對應 offset 的消息位置:

          1. 按照二分法找到小于 offset 的 segment 的.log 和.index
          2. 用目標 offset 減去文件名中的 offset 得到消息在這個 segment 中的偏移量。
          3. 再次用二分法在 index 文件中找到對應的索引。
          4. 到 log 文件中,順序查找,直到找到 offset 對應的消息。

          總結

          Kafka 是一個優(yōu)秀的開源項目。其在性能上面的優(yōu)化做的淋漓盡致,是很值得我們深入學習的一個項目。無論是思想還是實現(xiàn),我們都應該認真的去看一看,想一想。

          Kafka 性能優(yōu)化:

          1. 零拷貝網(wǎng)絡和磁盤
          2. 優(yōu)秀的網(wǎng)絡模型,基于 Java NIO
          3. 高效的文件數(shù)據(jù)結構設計
          4. Parition 并行和可擴展
          5. 數(shù)據(jù)批量傳輸
          6. 數(shù)據(jù)壓縮
          7. 順序讀寫磁盤
          8. 無鎖輕量級 offset
          瀏覽 90
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日日躁夜夜躁夜夜揉人人视频 | 翔田千里vs黑人播放 | 成人中文字幕在线 | 日韩操逼视频 | 精品人妻无码一区二区三区竹菊影视 |