Stories

Detail Return Return

別再亂排查了!Kafka 消息積壓、重複、丟失,根源基本都是 Rebalance! - Stories Detail

大家好,我是小富~

有次上線監控告警突然炸了,Kafka 訂單 Topic 消息積壓量突破 10 萬條,下游支付服務拿不到數據,部分用户付款後一直顯示處理中。

緊急登錄集羣排查,發現消費者組明明有 3 個節點,卻只有 1 個在正常消費,原來 10 分鐘前觸發了 Rebalance,另外兩個節點還卡在分區重新分配的狀態,導致消費能力直接砍半。

所以我的經驗是:Kafka出現消息積壓、重複、丟失這類問題,直接看是否有Rebalance,能解決大部分問題。

什麼時候會觸發 Rebalance?

Rebalance 本質是消費者組內分區與消費者的重新分配,只有當消費者、分區的對應關係被打破時才會觸發,下邊咱們看看幾種比較常見的場景:

1. 消費者數量變了(最頻繁)

擴容觸發:業務高峯時加了消費者節點,比如 3 個分區原本 2 個消費者承擔,新增 1 個後,需要重新分配成 1 個消費者對應 1 個分區;

下線觸發:消費者節點宕機、網絡斷連,或進程被誤殺,比如 3 個消費者少了 1 個,剩下 2 個要接手它的分區,必然觸發 Rebalance。

之前我們的日誌服務就踩過坑:K8s 節點資源不足,導致消費者 Pod 頻繁重啓,每重啓一次就觸發一次 Rebalance,消息積壓越來越嚴重。

2. Topic 分區數加了

Kafka 不支持減少分區,但新增分區時,已存在的消費者組不會自動感知新分區,必須通過 Rebalance,才能把新分區分配給組內消費者。

比如給 order-topic 從 5 個分區擴到 8 個,原本的消費者組只會消費舊的 5 個分區,直到觸發 Rebalance 後,才會接手新增的 3 個分區。

3. 訂閲的 Topic 變了

消費者組通過 subscribe() 訂閲 Topic 時,若修改訂閲列表(比如從只訂閲 order-topic,改成同時訂閲 order-topicpay-topic),會觸發 Rebalance,重新分配所有訂閲 Topic 的分區。

4. 心跳或消費超時(隱性坑)

消費者靠心跳向 Coordinator(協調者)證明自己活着,這兩個超時參數設不好,很容易觸發誤判式 Rebalance:

心跳超時:消費者每 3 秒(默認 heartbeat.interval.ms)發一次心跳,超過 45 秒(默認 session.timeout.ms)沒發,就被判定死亡;

消費超時:處理單批消息超過 5 分鐘(默認 max.poll.interval.ms),哪怕心跳正常,也會被強制踢出組,觸發 Rebalance。

我們之前處理大訂單消息時,單條消息處理要 6 分鐘,直接觸發消費超時,導致 Rebalance 頻繁發生。

Rebalance 引起哪些問題

Rebalance 不是瞬間完成的,整個過程要經歷註銷舊分區→選舉 Leader→分配新分區→消費者初始化,期間對業務的影響比你想的大。

1. 消費暫停,消息積壓

Rebalance 期間,所有消費者都會暫停消費,等待新的分區分配。如果消費者組規模大(比如 100 個消費者、1000 個分區),Rebalance 可能持續幾十秒,這段時間 Topic 消息只會堆積,下游服務拿不到數據。

所以在有消息積壓的情況,優先看看是否有 Rebalance 的情況。

2. 消息重複和消息丟失

Rebalance 後,消費者重新拿到分區時,消費進度可能倒退:若沒及時提交 offset(不管自動還是手動),會從最後一次提交的 offset 開始消費,中間沒提交的消息要麼重複處理,要麼直接跳過,也就是消息重複消費和消息丟失的原因。

極端情況(比如 Coordinator 宕機),offset 存儲的分區發生主從切換,可能導致 offset 數據錯亂,進度直接回到幾天前。

3. 資源浪費,負載不均

Rebalance 要靠 Coordinator 協調,頻繁觸發會佔用 Kafka 集羣的 CPU 和網絡資源;而且 Kafka 默認的分區分配策略(Range 或 RoundRobin),很容易導致負載不均。

比如 5 個分區分配給 2 個消費者,可能出現 3 個分區 vs 2 個分區的情況,其中一個消費者壓力翻倍,處理速度變慢,又會觸發新的 Rebalance,陷入惡性循環。

什麼情況下會丟數據

Rebalance 本身不會直接丟數據,但結合offset 提交和處理邏輯,很容易出現消息漏消費。

1.自動提交 offset + 消費沒完成

Kafka 默認自動提交 offset,提交時機是 poll 到消息後,等 5 秒(默認 auto.commit.interval.ms)自動提交。如果剛提交完 offset,消息還沒處理完就觸發 Rebalance,新消費者會從已提交的 offset 之後 開始消費,中間沒處理的消息就丟了。

舉個例子:

  • 消費者 A poll 到 offset 100-200 的消息,5 秒後自動提交 offset 200;
  • 處理到 150 條時,節點突然宕機,觸發 Rebalance;
  • 新消費者 B 從 offset 200 開始消費,offset 150-199 的消息直接丟失。

2. 手動提交 offset 時機錯了

手動提交時,如果把提交 offset 放在處理消息之前,也會丟數據。

  • 錯誤邏輯:先提交 offset → 再處理消息;
  • 風險:提交後、處理前觸發 Rebalance,新消費者會跳過已提交的消息,導致未處理的消息丟失。

正確的做法應該是先處理消息→再提交 offset,確保消息處理完才更新進度。

什麼情況下會重複消費?

相比丟數據,kafka Rebalance 導致的重複消費更普遍,核心原因都是 offset 提交滯後於消息處理。

1. 手動提交時,Rebalance 打斷了提交

開啓手動提交後,若在處理完消息→提交 offset 的間隙觸發 Rebalance,offset 沒提交成功,新消費者會從上次提交的位置重新消費。

  • 消費者 A 處理完 offset 100-200 的消息,準備提交時,因心跳超時被踢出組;
  • 新消費者 B 從 offset 100 開始消費,導致 100-200 的消息被重複處理。

2. 消費超時被踢,消息還在處理

處理消息耗時超過 max.poll.interval.ms,消費者被判定死亡,但實際還在處理消息。

  • 消費者 A 處理大消息用了 6 分鐘,超過默認 5 分鐘的超時時間,被踢出組;
  • 新消費者 B 接手分區,從上次提交的 offset 開始消費;
  • 消費者 A 後來處理完消息,想提交 offset 卻發現自己已被踢出,提交失敗,導致消息重複。

3. offset 找不到,回退到最早

如果消費者組的 auto.offset.reset 設為 earliest(默認是 latest),Rebalance 後找不到已提交的 offset(比如 offset 數據損壞),會從 Topic 最早的消息開始消費,導致歷史消息重複。

如何優化 Rebalance

Rebalance 這種情況是無法完全避免,不過我們可以來優化,能把影響降到最低。

1. 避免頻繁觸發 Rebalance

調優超時參數:根據消息處理耗時,把 max.poll.interval.ms 設大(比如大消息設為 10 分鐘),session.timeout.ms 設為 60-120 秒,避免誤判死亡;

保證消費者穩定:用監控盯緊消費者節點的 CPU、內存,避免 K8s Pod 頻繁重啓,或服務器宕機。

2. 安全處理 offset 提交

優先手動提交,關閉自動提交(enable.auto.commit=false),在消息處理完成後再調用 commitSync() 提交;

必要時用事務,如果業務不允許重複消費,結合 Kafka 事務,確保消息處理 和 offset 提交原子性。

3. 優化分區分配

選粘性分配策略:把 partition.assignment.strategy 設為 StickyAssignor,Rebalance 時儘量保留原有分配,減少分區變動。

4. 優化消費邏輯

做好冪等性:比如用訂單 ID 作為唯一鍵,即使重複消費,也不會導致業務邏輯出錯(比如重複扣錢、重複生成訂單)。

寫在最後

Rebalance 是面試的時候常愛問的場景題,它是 Kafka 消費者組的雙刃劍,用好能均衡負載,用不好就會引發故障,最後我總結下:

  1. 觸發 Rebalance 主要是消費者或分區變了或超時了;
  2. 丟數據和重複消費,本質是 offset 提交和 Rebalance 時機沒配合好;
  3. 優化超時參數、手動提交 offset、做好冪等性,是減少影響的關鍵。

看完等於學會,點個贊吧!!!

user avatar mannayang Avatar pulsgarney Avatar sofastack Avatar xuxueli Avatar lenglingx Avatar wanshoujidezhuantou Avatar yian Avatar tssc Avatar linybjikezhilu Avatar boxuegu Avatar wuliaodeliema Avatar wuliaodechaye Avatar
Favorites 60 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.