別再亂排查了!Kafka 消息積壓、重復(fù)、丟失,根源基本都是 Rebalance!

有次上線監(jiān)控告警突然炸了,Kafka 訂單 Topic 消息積壓量突破 10 萬條,下游支付服務(wù)拿不到數(shù)據(jù),部分用戶付款后一直顯示處理中。

緊急登錄集群排查,發(fā)現(xiàn)消費者組明明有 3 個節(jié)點,卻只有 1 個在正常消費,原來 10 分鐘前觸發(fā)了 Rebalance,另外兩個節(jié)點還卡在分區(qū)重新分配的狀態(tài),導(dǎo)致消費能力直接砍半。

所以我的經(jīng)驗是:Kafka出現(xiàn)消息積壓、重復(fù)、丟失這類問題,直接看是否有Rebalance,能解決大部分問題。

什么時候會觸發(fā) Rebalance?

Rebalance 本質(zhì)是消費者組內(nèi)分區(qū)與消費者的重新分配https://www.naquan.com/,只有當(dāng)消費者、分區(qū)的對應(yīng)關(guān)系被打破時才會觸發(fā),下邊咱們看看幾種比較常見的場景:

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

擴(kuò)容觸發(fā):業(yè)務(wù)高峰時加了消費者節(jié)點,比如 3 個分區(qū)原本 2 個消費者承擔(dān),新增 1 個后,需要重新分配成 1 個消費者對應(yīng) 1 個分區(qū);

下線觸發(fā):消費者節(jié)點宕機(jī)、網(wǎng)絡(luò)斷連,或進(jìn)程被誤殺,比如 3 個消費者少了 1 個,剩下 2 個要接手它的分區(qū),必然觸發(fā) Rebalance。

之前我們的日志服務(wù)就踩過坑:K8s 節(jié)點資源不足,導(dǎo)致消費者 Pod 頻繁重啟,每重啟一次就觸發(fā)一次 Rebalance,消息積壓越來越嚴(yán)重。

2. Topic 分區(qū)數(shù)加了

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

比如給 order-topic 從 5 個分區(qū)擴(kuò)到 8 個,原本的消費者組只會消費舊的 5 個分區(qū),直到觸發(fā) Rebalance 后,才會接手新增的 3 個分區(qū)。

3. 訂閱的 Topic 變了

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

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

消費者靠心跳向 Coordinator(協(xié)調(diào)者)證明自己活著,這兩個超時參數(shù)設(shè)不好,很容易觸發(fā)誤判式 Rebalance:

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

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

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

Rebalance 引起哪些問題

Rebalance 不是瞬間完成的,整個過程要經(jīng)歷注銷舊分區(qū)→選舉 Leader→分配新分區(qū)→消費者初始化,期間對業(yè)務(wù)的影響比你想的大。

1. 消費暫停,消息積壓

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

所以在有消息積壓的情況,優(yōu)先看看是否有 Rebalance 的情況。

2. 消息重復(fù)和消息丟失

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

極端情況(比如 Coordinator 宕機(jī)),offset 存儲的分區(qū)發(fā)生主從切換,可能導(dǎo)致 offset 數(shù)據(jù)錯亂,進(jìn)度直接回到幾天前。

3. 資源浪費,負(fù)載不均

Rebalance 要靠 Coordinator 協(xié)調(diào),頻繁觸發(fā)會占用 Kafka 集群的 CPU 和網(wǎng)絡(luò)資源;而且 Kafka 默認(rèn)的分區(qū)分配策略(Range 或 RoundRobin),很容易導(dǎo)致負(fù)載不均。

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

什么情況下會丟數(shù)據(jù)

Rebalance 本身不會直接丟數(shù)據(jù),但結(jié)合offset 提交和處理邏輯,很容易出現(xiàn)消息漏消費。

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

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

舉個例子:

消費者 A poll 到 offset 100-200 的消息,5 秒后自動提交 offset 200;

處理到 150 條時,節(jié)點突然宕機(jī),觸發(fā) Rebalance;

新消費者 B 從 offset 200 開始消費,offset 150-199 的消息直接丟失。

2. 手動提交 offset 時機(jī)錯了

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

錯誤邏輯:先提交 offset → 再處理消息;

風(fēng)險:提交后、處理前觸發(fā) Rebalance,新消費者會跳過已提交的消息,導(dǎo)致未處理的消息丟失。

正確的做法應(yīng)該是先處理消息→再提交 offset,確保消息處理完才更新進(jìn)度。

什么情況下會重復(fù)消費?

相比丟數(shù)據(jù),kafka Rebalance 導(dǎo)致的重復(fù)消費更普遍,核心原因都是 offset 提交滯后于消息處理。

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

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

消費者 A 處理完 offset 100-200 的消息,準(zhǔn)備提交時,因心跳超時被踢出組;

新消費者 B 從 offset 100 開始消費,導(dǎo)致 100-200 的消息被重復(fù)處理。

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

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

消費者 A 處理大消息用了 6 分鐘,超過默認(rèn) 5 分鐘的超時時間,被踢出組;

新消費者 B 接手分區(qū),從上次提交的 offset 開始消費;

消費者 A 后來處理完消息,想提交 offset 卻發(fā)現(xiàn)自己已被踢出,提交失敗,導(dǎo)致消息重復(fù)。

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

如果消費者組的 auto.offset.reset 設(shè)為 earliest(默認(rèn)是 latest),Rebalance 后找不到已提交的 offset(比如 offset 數(shù)據(jù)損壞),會從 Topic 最早的消息開始消費,導(dǎo)致歷史消息重復(fù)。

如何優(yōu)化 Rebalance

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

1. 避免頻繁觸發(fā) Rebalance

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

保證消費者穩(wěn)定:用監(jiān)控盯緊消費者節(jié)點的 CPU、內(nèi)存,避免 K8s Pod 頻繁重啟,或服務(wù)器宕機(jī)。

2. 安全處理 offset 提交

優(yōu)先手動提交,關(guān)閉自動提交(enable.auto.commit=false),在消息處理完成后再調(diào)用 commitSync() 提交;

必要時用事務(wù),如果業(yè)務(wù)不允許重復(fù)消費,結(jié)合 Kafka 事務(wù),確保消息處理 和 offset 提交原子性。

3. 優(yōu)化分區(qū)分配

選粘性分配策略:把 partition.assignment.strategy 設(shè)為 StickyAssignor,Rebalance 時盡量保留原有分配,減少分區(qū)變動。

4. 優(yōu)化消費邏輯

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

寫在最后

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

觸發(fā) Rebalance 主要是消費者或分區(qū)變了或超時了;

丟數(shù)據(jù)和重復(fù)消費,本質(zhì)是 offset 提交和 Rebalance 時機(jī)沒配合好;

優(yōu)化超時參數(shù)、手動提交 offset、做好冪等性,是減少影響的關(guān)鍵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容