聊聊 Kafka Consumer 那點事

LNMPRG原始碼研究發表於2021-10-17

作者:王江華 原文:https://mp.weixin.qq.com/s/jS...
image.png

在上一篇中我們詳細聊了關於 Kafka Producer 內部的底層原理設計思想和細節, 本篇我們主要來聊聊 Kafka Consumer 即消費者的內部底層原理設計思想。

1、Consumer之總體概述

在 Kafka 中, 我們把消費訊息的一方稱為 Consumer 即 消費者, 它是 Kafka 的核心元件之一。它的主要功能是將 Producer 生產的訊息進行消費處理,完成消費任務。 那麼這些 Producer 產生的訊息是怎麼被 Consumer 消費的呢?又是基於何種消費方式進行消費,分割槽分配策略都有哪些,消費者組以及重平衡機制是如何處理的,偏移量如何提交和儲存,消費進度如何監控, 如何保證消費處理完成?接下來會逐一講解說明。

2、Consumer之消費方式詳解

我們知道訊息佇列一般有兩種實現方式,(1)Push(推模式) (2)Pull(拉模式),那麼 Kafka Consumer 究竟採用哪種方式進行消費的呢?其實 Kafka Consumer 採用的是主動拉取 Broker 資料進行消費的即 Pull 模式。 這兩種方式各有優劣,我們來分析一下:

1)、為什麼不採用Push模式? 如果是選擇 Push 模式最大缺點就是 Broker 不清楚 Consumer 的消費速度,且推送速率是 Broker 進行控制的, 這樣很容易造成訊息堆積,如果 Consumer 中執行的任務操作是比較耗時的,那麼 Consumer 就會處理的很慢, 嚴重情況可能會導致系統 Crash。

2)、為什麼採用Pull模式? 如果選擇 Pull 模式,這時 Consumer 可以根據自己的情況和狀態來拉取資料, 也可以進行延遲處理。但是 Pull 模式也有不足,Kafka 又是如何解決這一問題?如果 Kafka Broker 沒有訊息,這時每次 Consumer 拉取的都是空資料, 可能會一直迴圈返回空資料。 針對這個問題,Consumer 在每次呼叫 Poll() 消費資料的時候,順帶一個 timeout 引數,當返回空資料的時候,會在 Long Polling 中進行阻塞,等待 timeout 再去消費,直到資料到達。

3、Consumer之初始化

聊完 Consumer 消費方式和優缺點以及 Kafka 針對缺點又是如何權衡解決的,接下來我們來聊聊 Consumer初始化都做了什麼?

首先看一下Kafka consumer 初始化程式碼:
image.png

從程式碼可以看出初始化 Consumer 有4步:

  • 1、構造 Propertity 物件,進行 Consumer 相關的配置;
  • 2、建立 KafkaConsumer 的物件 Consumer;
  • 3、訂閱相應的 Topic 列表;
  • 4、呼叫 Consumer 的 poll() 方法拉取訂閱的訊息

Kafka consumer 消費流程圖如下:
image.png

4、Consumer之消費者組機制

4.1 Consumer Group機制

聊完 Consumer 的初始化流程,接下來我們來聊聊 Consumer 消費者組機制,為什麼 Kafka 要設計 Consumer Group, 只有 Consumer 不可以嗎? 我們知道 Kafka 是一款高吞吐量,低延遲,高併發, 高可擴充套件性的訊息佇列產品, 那麼如果某個 Topic 擁有數百萬到數千萬的資料量, 僅僅依靠 Consumer 程式消費, 消費速度可想而知, 所以需要一個擴充套件性較好的機制來保障消費進度, 這個時候 Consumer Group 應運而生, Consumer Group 是 Kafka 提供的可擴充套件且具有容錯性的消費者機制。

Kafka Consumer Group 特點如下:

  • 1、每個 Consumer Group 有一個或者多個 Consumer
  • 2、每個 Consumer Group 擁有一個公共且唯一的 Group ID
  • 3、Consumer Group 在消費 Topic 的時候,Topic 的每個 Partition 只能分配給組內的某個 Consumer,只要被任何 Consumer 消費一次, 那麼這條資料就可以認為被當前 Consumer Group 消費成功

4.2 Partition 分配策略機制

我們知道一個 Consumer Group 中有多個 Consumer,一個 Topic 也有多個 Partition,所以必然會涉及到 Partition 的分配問題: 確定哪個 Partition 由哪個 Consumer 來消費的問題。

Kafka 客戶端提供了3 種分割槽分配策略:RangeAssignor、RoundRobinAssignor 和 StickyAssignor,前兩種分配方案相對簡單一些StickyAssignor 分配方案相對複雜一些。

4.2.1 RangeAssignor

RangeAssignor 是 Kafka 預設的分割槽分配演算法,它是按照 Topic 的維度進行分配的,對於每個 Topic,首先對 Partition 按照分割槽ID進行排序,然後對訂閱這個 Topic 的 Consumer Group 的 Consumer 再進行排序,之後儘量均衡的按照範圍區段將分割槽分配給 Consumer。此時可能會造成先分配分割槽的 Consumer 程式的任務過重(分割槽數無法被消費者數量整除)。

分割槽分配場景分析如下圖所示(同一個消費者組下的多個 consumer):

image.png

image.png

image.png

結論:這種分配方式明顯的問題就是隨著消費者訂閱的Topic的數量的增加,不均衡的問題會越來越嚴重。

4.2.2 RoundRobinAssignor

RoundRobinAssignor 的分割槽分配策略是將 Consumer Group 內訂閱的所有 Topic 的 Partition 及所有 Consumer 進行排序後按照順序儘量均衡的一個一個進行分配。如果 Consumer Group 內,每個 Consumer 訂閱都訂閱了相同的Topic,那麼分配結果是均衡的。如果訂閱 Topic 是不同的,那麼分配結果是不保證“儘量均衡”的,因為某些 Consumer 可能不參與一些 Topic 的分配。

分割槽分配場景分析如下圖所示:
1) 當組內每個 Consumer 訂閱的 Topic 是相同情況:
image.png

2) 當組內每個訂閱的 Topic 是不同情況,這樣就可能會造成分割槽訂閱的傾斜:
image.png

4.2.3 StickyAssignor

StickyAssignor 分割槽分配演算法是 Kafka Java 客戶端提供的分配策略中最複雜的一種,可以通過 partition.assignment.strategy 引數去設定,從 0.11 版本開始引入,目的就是在執行新分配時,儘量在上一次分配結果上少做調整,其主要實現了以下2個目標:

1)、Topic Partition 的分配要儘量均衡。

2)、當 Rebalance(重分配,後面會詳細分析) 發生時,儘量與上一次分配結果保持一致。

注意:當兩個目標發生衝突的時候,優先保證第一個目標,這樣可以使分配更加均勻,其中第一個目標是3種分配策略都儘量去嘗試完成的, 而第二個目標才是該演算法的精髓所在。

下面我們舉例來聊聊 RoundRobinAssignor 跟 StickyAssignor的區別。

分割槽分配場景分析如下圖所示:

1)組內每個 Consumer 訂閱的 Topic 是相同情況,RoundRobinAssignor 跟StickyAssignor 分配一致:
image.png

當上述情況發生 Rebalance 情況後,可能分配會不太一樣,假如這時候C1發生故障下線:

RoundRobinAssignor:
image.png

而StickyAssignor:
image.png

結論: 從上面 Rebalance 後的結果可以看出,雖然兩種分配策略最後都是均勻分配的,但是 RoundRoubinAssignor 完全是重新分配了一遍,而 StickyAssignor 則是在原先的基礎上達到了均勻的狀態。

2) 當組內每個 Consumer 訂閱的 Topic 是不同情況:

RoundRobinAssignor:
image.png

StickyAssignor:
image.png

當上述情況發生 Rebalance 情況後,可能分配會不太一樣,假如這時候C1發生故障下線:

RoundRobinAssignor:
image.png
StickyAssignor:
image.png

從上面結果可以看出,RoundRoubin 的分配策略在 Rebalance (重分配)之後造成了嚴重的分配傾斜。因此在生產環境上如果想要減少重分配帶來的開銷,可以選用 StickyAssignor 的分割槽分配策略。

5、Consumer之消費者組重分配機制

上面聊完消費者組以及分割槽分配策略後,我們來聊聊 Consumer Group 中 Rebalance (重分配) 機制,對於 Consumer Group 來說,可能隨時都會有 Consumer 加入或退出,那麼 Consumer 列表的變化必定會引起 Partition 的重新分配。我們將這個分配過程叫做 Consumer Rebalance,但是這個分配過程需要藉助 Broker 端的 Coordinator 協調者元件,在 Coordinator 的幫助下完成整個消費者組的分割槽重分配,也是通過監聽ZooKeeper 的 /admin/reassign_partitions 節點觸發的。

5.1 Rebalance 觸發與通知

Rebalance 的觸發條件有三種:

  • 1、當 Consumer Group 組成員數量發生變化(主動加入或者主動離組,故障下線等)
  • 2、當訂閱主題數量發生變化
  • 3、當訂閱主題的分割槽數發生變化

Rebalance 如何通知其他 consumer 程式?

Rebalance 的通知機制就是靠 Consumer 端的心跳執行緒,它會定期傳送心跳請求到 Broker 端的 Coordinator,當協調者決定開啟 Rebalance 後,它會將“REBALANCE_IN_PROGRESS”封裝進心跳請求的響應中傳送給 Consumer ,當 Consumer 發現心跳響應中包含了“REBALANCE_IN_PROGRESS”,就知道 Rebalance 開始了。

5.2 協議 (protocol) 說明

其實 Rebalance 本質上也是一組協議。Consumer Group 與 Coordinator 共同使用它來完成 Consumer Group 的 Rebalance。下面我看看這5種協議都是什麼,完成了什麼功能:

  • 1、Heartbeat請求:Consumer 需要定期給 Coordinator 傳送心跳來證明自己還活著。
  • 2、LeaveGroup請求:主動告訴 Coordinator 要離開 Consumer Group
  • 3、SyncGroup請求:Group Leader Consumer 把分配方案告訴組內所有成員
  • 4、JoinGroup請求:成員請求加入組
  • 5、DescribeGroup請求:顯示組的所有資訊,包括成員資訊,協議名稱,分配方案,訂閱資訊等。通常該請求是給管理員使用。

Coordinator 在 Rebalance 的時候主要用到了前面4種請求

5.3 Consumer Group 狀態機

Rebalance 一旦發生,必定會涉及到 Consumer Group 的狀態流轉,此時 Kafka 為我們設計了一套完整的狀態機機制,來幫助 Broker Coordinator 完成整個重平衡流程。瞭解整個狀態流轉過程可以幫助我們深入理解 Consumer Group 的設計原理。

5種狀態,定義分別如下:

Empty 狀態

 Empty 狀態表示當前組內無成員, 但是可能存在 Consumer Group 已提交的位移資料,且未過期,這種狀態只能響應 JoinGroup 請求。

Dead 狀態

Dead 狀態表示組內已經沒有任何成員的狀態,組內的後設資料已經被 Broker Coordinator 移除,這種狀態響應各種請求都是一個Response:UNKNOWN_MEMBER_ID。

PreparingRebalance 狀態

 PreparingRebalance 狀態表示準備開始新的 Rebalance, 等待組內所有成員重新加入組內。

CompletingRebalance 狀態

 CompletingRebalance 狀態表示組內成員都已經加入成功,正在等待分配方案,舊版本中叫“AwaitingSync”。

Stable 狀態

 Stable 狀態表示 Rebalance 已經完成, 組內 Consumer 可以開始消費了。

5種狀態流轉圖如下:
image.png

5.4 Rebalance 流程分析

接下來我們看看 Rebalance 的流程,通過上面5種狀態可以看出,Rebalance 主要分為兩個步驟:加入組(對應JoinGroup請求)和等待 Leader Consumer 分配方案(SyncGroup 請求)。

1)、JoinGroup 請求: 組內所有成員向 Coordinator 傳送 JoinGroup 請求,請求加入組,順帶會上報自己訂閱的 Topic,這樣 Coordinator 就能收集到所有成員的 JoinGroup 請求和訂閱 Topic 資訊,Coordinator 就會從這些成員中選擇一個擔任這個Consumer Group 的 Leader(一般情況下,第一個傳送請求的 Consumer 會成為 Leader),這裡說的Leader 是指具體的某一個 consumer,它的任務就是收集所有成員的訂閱 Topic 資訊,然後制定具體的消費分割槽分配方案。 待選出 Leader 後,Coordinator 會把 Consumer Group 的訂閱 Topic 資訊封裝進 JoinGroup 請求的 Response 中,然後發給 Leader ,然後由 Leader 統一做出分配方案後,進入到下一步,如下圖:
image.png

2)、SyncGroup 請求: Leader 開始分配消費方案,即哪個 Consumer 負責消費哪些 Topic 的哪些 Partition。 一旦完成分配,Leader 會將這個分配方案封裝進 SyncGroup 請求中發給 Coordinator ,其他成員也會發 SyncGroup 請求,只是內容為空,待 Coordinator 接收到分配方案之後會把方案封裝進 SyncGroup 的 Response 中發給組內各成員, 這樣各自就知道應該消費哪些 Partition 了,如下圖:
image.png

5.5 Rebalance 場景分析

剛剛詳細的聊了關於 Rebalance 的狀態流轉與流程分析,接下來我們通過時序圖來重點分析幾個場景來加深對 Rebalance 的理解。

場景一:新成員(c1)加入組
image.png

場景二:成員(c2)主動離組
image.png

場景三:成員(c2)超時被踢出組
image.png

場景四:成員(c2)提交位移資料
image.png

6、 Consumer之位移提交機制

6.1 位移提交 Offset 概念理解

上面聊完消費者組 Rebalance 機制後,我們來聊聊 Consumer 的位移提交機制,在聊位移提交之前,我們回顧一下 位移 和 消費者位移 之間的區別。通常所說的位移是指 Topic Partition 在 Broker 端的儲存偏移量,而消費者位移則是指某個 Consumer Group 在不同 Topic Partition 上面的消費偏移量(也可以理解為消費進度),它記錄了 Consumer 要消費的下一條訊息的位移。

Consumer 需要向 Kafka 上報自己的位移資料資訊,我們將這個上報過程叫做提交位移(Committing Offsets)。它是為了保證 Consumer的消費進度正常,當 Consumer 發生故障重啟後, 可以直接從之前提交的 Offset 位置開始進行消費而不用重頭再來一遍(Kafka 認為小於提交的 Offset 的訊息都已經成功消費了),Kafka 設計了這個機制來保障消費進度。我們知道 Consumer 可以同時去消費多個分割槽的資料,所以位移提交是按照分割槽的粒度進行上報的,也就是說 Consumer 需要為分配給它的每個分割槽提交各自的位移資料。

6.2 多種提交方式分析

Kafka Consumer 提供了多種提交方式,從使用者角度來說:位移提交可以分為自動提交和手動提交,但從 Consumer 的角度來說,位移提交可以分為同步提交和非同步提交, 接下來我們就來聊聊自動提交和手動提交方式:

自動提交
自動提交是指 Kafka Consumer 在後臺默默地幫我們提交位移,使用者不需要關心這個事情。啟用自動提交位移,在 初始化 KafkaConsumer 的時候,通過設定引數 enable.auto.commit = true (預設為true),開啟之後還需要另外一個引數進行配合即 auto.commit.interval.ms,這個參數列示 Kafka Consumer 每隔 X 秒自動提交一次位移,這個值預設是5秒。

自動提交看起來是挺美好的, 那麼自動提交會不會出現消費資料丟失的情況呢?在設定了 enable.auto.commit = true 的時候,Kafka 會保證在開始呼叫 Poll() 方法時,提交上一批訊息的位移,再處理下一批訊息, 因此它能保證不出現消費丟失的情況。但自動提交位移也有設計缺陷,那就是它可能會出現重複消費。就是在自動提交間隔之間發生 Rebalance 的時候,此時 Offset 還未提交,待 Rebalance 完成後, 所有 Consumer 需要將發生 Rebalance 前的訊息進行重新消費一次。

手動提交
與自動提交相對應的就是手動提交了。開啟手動提交位移的方法就是在初始化KafkaConsumer 的時候設定引數 enable.auto.commit = false,但是隻設定為 false 還不行,它只是告訴 Kafka Consumer 不用自動提交位移了,你還需要在處理完訊息之後呼叫相應的 Consumer API 手動進行提交位移,對於手動提交位移,又分為同步提交和非同步提交。

1)、同步提交API:

KafkaConsumer#commitSync(),該方法會提交由 KafkaConsumer#poll() 方法返回的最新位移值,它是一個同步操作,會一直阻塞等待直到位移被成功提交才返回,如果提交的過程中出現異常,該方法會將異常丟擲。這裡我們知道在呼叫 commitSync() 方法的時機是在處理完 Poll() 方法返回所有訊息之後進行提交,如果過早的提交了位移就會出現消費資料丟失的情況。

2)、非同步提交API:

KafkaConsumer#commitAsync(),該方法是非同步方式進行提交的,呼叫 commitAsync() 之後,它會立即返回,並不會阻塞,因此不會影響 Consumer 的 TPS。另外 Kafka 針對它提供了callback,方便我們來實現提交之後的邏輯,比如記錄日誌或異常處理等等。由於它是一個非同步操作, 假如出現問題是不會進行重試的,這時候重試位移值可能已不是最新值,所以重試無意義。

3)、混合提交模式:

從上面分析可以得出 commitSync 和 commitAsync 都有自己的缺陷,我們需要將 commitSync 和 commitAsync 組合使用才能到達最理想的效果,既不影響 Consumer TPS,又能利用 commitSync 的自動重試功能來避免一些瞬時錯誤(網路抖動,GC,Rebalance 問題),在生產環境中建議大家使用混合提交模式來提高 Consumer的健壯性。

7、Consumer之__consumer_offsets儲存

7.1 __consumer_offsets 揭祕

上面聊完 Consumer 位移提交,我們知道 Consumer 消費完資料後需要進行位移提交, 那麼提交的位移資料究竟儲存在哪裡, 又是以何種方式進行儲存的,接下來我們就看看新舊版本 Kafka 對於 Offset 儲存方式。

我們知道 Kafka 舊版本(0.8版本之前)是重度依賴 Zookeeper 來實現各種各樣的協調管理,當然舊版本的 Consumer Group 是把位移儲存在 ZooKeeper 中,減少 Broker 端狀態儲存開銷,鑑於 Zookeeper 的儲存架構設計來說, 它不適合頻繁寫更新,而 Consumer Group 的位移提交又是高頻寫操作,這樣會拖慢 ZooKeeper 叢集的效能, 於是在新版 Kafka 中, 社群重新設計了 Consumer Group 的位移管理方式,採用了將位移儲存在 Kafka 內部(這是因為 Kafka Topic 天然支援高頻寫且持久化),這就是所謂大名鼎鼎的__consumer_offsets。

__consumer_offsets:用來儲存 Kafka Consumer 提交的位移資訊,另外它是由 Kafka 自動建立的,和普通的 Topic 相同,它的訊息格式也是 Kafka 自己定義的,我們無法進行修改。這裡我們很好奇它的訊息格式究竟是怎麼樣的,讓我們來一起分析並揭開它的神祕面紗吧。

__consumer_offsets 訊息格式分析揭祕:

  • 1、所謂的訊息格式我們可以簡單理解為是一個 KV 對。Key 和 Value 分別表示訊息的鍵值和訊息體。
  • 2、那麼 Key 存什麼呢?既然是儲存 Consumer 的位移資訊,在 Kafka 中,Consumer 數量會很多,那麼必須有欄位來標識這個位移資料是屬於哪個 Consumer的,怎麼來標識 Consumer 欄位呢?前面在講解 Consumer Group 的時候我們知道它共享一個公共且唯一的Group ID,那麼只儲存它就可以了嗎?我們知道 Consumer 提交位移是在分割槽的維度進行的,很顯然,key中還應該儲存 Consumer 要提交位移的分割槽。
  • 3、總結:位移主題的 Key 中應該儲存 3 部分內容:<Group ID,主題名,分割槽號 >
  • 4、value 可以簡單認為儲存的是offset值,當然底層還儲存其他一些後設資料,幫助 Kafka 來完成一些其他操作,比如刪除過期位移資料等。

__consumer_offsets 訊息格式示意圖:

image.png
image.png
image.png

7.2 __consumer_offsets建立過程

聊完訊息格式後, 我們來聊聊 __consumer_offsets 是怎麼被建立出來的呢? 當 Kafka 叢集中的第一個 Consumer 啟動時,Kafka 會自動建立__consumer_offsets。前面說過,它就是普通的 Topic, 它也有對應的分割槽數,如果由 Kafka 自動建立的,那麼分割槽數又是怎麼設定的呢?這個依賴 Broker 端引數 offsets.topic.num.partitions (預設值是50),因此 Kafka 會自動建立一個有 50 個分割槽的__consumer_offsets 。這就是我們在 Kafka 日誌路徑下看到有很多 __consumer_offsets-xxx 這樣的目錄的原因。既然有分割槽數,必然就會有對應的副本數,這個是依賴 Broker 端另一個引數 offsets.topic.replication.factor(預設值為3)。總結一下,如果__consumer_offsets 由 Kafka 自動建立的,那麼該 Topic 的分割槽數是 50,副本數是 3,而具體 Group 的消費情況要儲存到哪個 Partition ,根據abs(GroupId.hashCode()) % NumPartitions 來計算的,這樣就可以保證 Consumer Offset 資訊與 Consumer Group 對應的 Coordinator 處於同一個 Broker 節點上。

7.3 檢視__consumer_offsets資料

Kafka 預設提供了指令碼供使用者檢視 Consumer 資訊, 具體的檢視方式如下:

//1.檢視 kafka 消費者組列表:
./bin/kafka-consumer-groups.sh --bootstrap-server <kafka-ip>:9092 --list

//2.檢視 kafka 中某一個消費者組(test-group-1)的消費情況:
./bin/kafka-consumer-groups.sh --bootstrap-server <kafka-ip>:9092 --group test-group-1 --describe

//3.計算 group.id 對應的 partition 的公式為:
abs(GroupId.hashCode()) % NumPartitions //其中GroupId:test-group-1 NumPartitions:50

//3.找到 group.id 對應的 partition 後,就可以指定分割槽消費了
//kafka 0.11以後
./bin/kafka-console-consumer.sh --bootstrap-server message-1:9092 --topic __consumer_offsets --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" --partition xx
//kafka 0.11以前
./bin/kafka-console-consumer.sh --bootstrap-server message-1:9092 --topic __consumer_offsets --formatter "kafka.coordinator.GroupMetadataManager\$OffsetsMessageFormatter" --partition xx

//4.獲取指定consumer group的位移資訊 
//kafka 0.11以後
kafka-simple-consumer-shell.sh --topic __consumer_offsets --partition xx --broker-list <kafka-ip>:9092 --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter"
//kafka 0.11以前
kafka-simple-consumer-shell.sh --topic __consumer_offsets --partition xx --broker-list <kafka-ip>:9092 --formatter "kafka.coordinator.GroupMetadataManager\$OffsetsMessageFormatter"

//5.指令碼執行後輸出的後設資料資訊 
//格式:[消費者組 : 消費的topic : 消費的分割槽] :: [offset位移], [offset提交時間], [後設資料過期時間]
[order-group-1,topic-order,0]::[OffsetMetadata[36672,NO_METADATA],CommitTime 1633694193000,ExpirationTime 1633866993000]

8、Consumer之消費進度監控

上面聊完 Consumer的各個實現細節,我們來聊聊對於 Consumer 來說,最重要的事情即消費進度的監控, 或者說監控其滯後程度(Consumer 當前落後於 Producer 的程度),這裡有個專業名詞叫 Consumer Lag。舉例說明: Kafka Producer 向某 Topic 成功生產了 1000 萬條訊息,這時 Consumer 當前消費了 900 萬條訊息,那麼可以認為 Consumer 滯後了 100 萬條訊息,即 Lag 等於 100 萬。

對 Consumer 來說,Lag 應該算是最重要的監控指標了。它直接反映了一個 Consumer 的執行情況。Lag 值越小表示該 Consumer 能夠及時的消費 Producer 生產出來的訊息,滯後程度很小;如果該值有增大的趨勢說明可能會有堆積,嚴重會拖慢下游的處理速度。

對於這麼重要的指標,我們該怎麼監控它呢?主要有 以下幾 種方法:

  • 1、使用 Kafka 自帶的命令列工具 kafka-consumer-groups 指令碼
  • 2、使用 Kafka Java Consumer API 程式設計
  • 3、使用 Kafka 自帶的 JMX 監控指標
  • 4、如果是雲產品的話, 可以直接使用雲產品自帶的監控功能

9、Consumer之總結

至此已經跟大家全面深入的剖析了 Kafka Consumer 內部底層原理設計的方方面面, kafka 原理相關篇章到此告一段落, 後續會針對 Kafka 細節技術點進行專題和原始碼分析, 大家敬請期待...

堅持總結, 持續輸出高質量文章 關注我: 華仔聊技術
image.png

相關文章