大家好,我是小富~
有次上線監控告警突然炸了,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-topic 和 pay-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 消費者組的雙刃劍,用好能均衡負載,用不好就會引發故障,最後我總結下:
- 觸發 Rebalance 主要是消費者或分區變了或超時了;
- 丟數據和重複消費,本質是 offset 提交和 Rebalance 時機沒配合好;
- 優化超時參數、手動提交 offset、做好冪等性,是減少影響的關鍵。
看完等於學會,點個贊吧!!!