年底 Kafka 突然積壓,我一度懷疑老板不想發(fā)年終獎(jiǎng)

快到年底了,系統(tǒng)開始頻繁出問題。

我有一個(gè)非常合理、非常工程師的懷疑

老板不想發(fā)年終獎(jiǎng),所以系統(tǒng)開始搞事。

果不其然—— 幾年都遇不到一次的 Kafka 消息積壓, 在一個(gè)本該安靜下班的夜晚, 卷土重來了。

今晚,注定是個(gè)不眠夜。

原神啟動(dòng)之前,我先啟動(dòng)了 Kafka 面板。

-****01-

**事故現(xiàn)場(chǎng) **

事情是這樣的。

我剛下班,正準(zhǔn)備洗洗睡,

組里的小伙伴突然沖過來,語氣已經(jīng)帶點(diǎn)顫:

“Kafka 消息積壓一直在漲,預(yù)覽圖全出不來!”

我點(diǎn)開面板一看,血壓直接上來了:

  • 原來 4 個(gè)分區(qū)

  • 已積壓 1200+

  • 新加的分區(qū)

  • 也開始積壓

  • 而且 速度越來越快

第一反應(yīng)非常自然,也非?!靶率钟押谩保?/p>

是不是消費(fèi)者慢?那我多加點(diǎn)實(shí)例不就完了?

于是我:

  • 加 Pod

  • 消費(fèi)能跑

  • 然后……

越跑越卡 Pod 開始一個(gè)接一個(gè)掛

這時(shí),我的困意 和不祥的預(yù)感 同時(shí)達(dá)到了頂峰。

[圖片上傳失敗...(image-6a4696-1767778965506)]

-****02-

**第一層誤判 **

我突然想起一件事:

Spring Cloud Stream 好像支持并發(fā)消費(fèi)?

于是讓開發(fā)老哥把:

<pre data-start="866" data-end="893" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

concurrency:10

</pre>

一把拉滿。

結(jié)果呢?

這時(shí)候我才反應(yīng)過來一個(gè)致命誤解

****concurrency ≠ 并行處理一條消息****

而是:

我人當(dāng)場(chǎng)清醒了。

  • concurrency = 消費(fèi)者線程數(shù)

  • 一個(gè)線程 ≈ 一個(gè)分區(qū)

  • 分區(qū)本來就不均勻

  • 一加線程 流量?jī)A斜直接拉滿

  • 消息 積壓更快

  • Pod 直接被打爆

  • CPU、內(nèi)存 一起起飛

[圖片上傳失敗...(image-a21ac6-1767778965506)]

-****03-

詭異現(xiàn)象

我把所有 Pod 日志全拉下來,一條一條看。

結(jié)果非常魔幻。

監(jiān)聽器日志顯示:

全部執(zhí)行成功

但與此同時(shí):

  • Kafka 報(bào) 消費(fèi)超時(shí)

  • 面板顯示 Consumer Group 頻繁 Rebalance

我當(dāng)場(chǎng)愣住。

成功了,又超時(shí)?

這是什么?

薛定諤的 Kafka 消費(fèi)?

但作為一個(gè)堅(jiān)定的唯物主義程序員, 我選擇繼續(xù)往下挖。

[圖片上傳失敗...(image-f454af-1767778965505)]

-****04-****破案關(guān)鍵

答案,藏在 Kafka 的消費(fèi)模型里。

你以為的 Kafka 是這樣的:

來一條 → 消費(fèi)一條 → 確認(rèn)一條

但實(shí)際上,Kafka 是這樣的:

一次 poll 一批 → 全部處理完 → 才提交 offset

Spring Cloud Stream,為了“好用”, 干了一件非常容易坑人的事

底層是批量拉取,但監(jiān)聽器只給你一條

我們假設(shè)一個(gè)真實(shí)到不能再真實(shí)的配置:

  • max.poll.records = 500

  • 單條消息處理:10s

  • 處理方式:串行

  • 消費(fèi)超時(shí)時(shí)間:300s

那會(huì)發(fā)生什么?

500 × 10s = 5000s

也就是說:

  • 一次 poll

  • 最多只能處理 30 條

  • 后面的消息根本來不及

于是就出現(xiàn)了那一幕:

  • 單條邏輯: 成功

  • 整批消費(fèi): 超時(shí)

  • Kafka 認(rèn)為你“失聯(lián)”

  • 觸發(fā) Consumer Rebalance

  • offset 不提交

  • 后面的消息 全部堵死

我當(dāng)場(chǎng)只想說一句:

我咧個(gè)豆,案子破了。

[圖片上傳失敗...(image-2a52d2-1767778965503)]

-****05-****兩種解決方案

方案一:立刻止血(適合半夜)

<pre data-start="1963" data-end="1991" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

ack-mode:RECORD

</pre>

效果:

  • 每條消息處理完立刻提交

  • 不再被“批次”拖死

  • 改一行就能下班睡覺

代價(jià):

  • 吞吐量下降

  • Kafka 的優(yōu)勢(shì)用不滿

適合場(chǎng)景:

救火 保命 保年終獎(jiǎng)

方案二:批量 + 并行(推薦)

思路:

批量要小,并行要真

1. 控制批量大小

max.poll.records:50

2. 自己并行處理這一批

@StreamListener("<TOPIC>")

效果:

  • 批次不大,不超時(shí)

  • 真正并行,吞吐拉滿

  • offset 提交穩(wěn)定

  • Kafka 安靜了,世界也安靜了

[圖片上傳失敗...(image-44c922-1767778965502)]

-****06-****總結(jié)

這次事故教會(huì)我的三件事

1. Kafka 慢,80% 不是 Kafka 的鍋

而是你 消費(fèi)模型 + 超時(shí)配置 + 批量大小 從來沒想清楚。

2. Spring Cloud Stream 很友好

但:

越像“隊(duì)列”的封裝,越容易誤導(dǎo)你

3. 半夜事故,拼的不是手速

而是你 對(duì)底層機(jī)制的理解深度


那天問題解決的時(shí)候,已經(jīng)快天亮了。

  • 咖啡喝完了

  • Kafka 面板綠了

  • 飛書安靜了

我終于能安心睡覺了。

如果你也遇到過:

  • Kafka 消息積壓

  • 日志成功但一直超時(shí)

  • Consumer Rebalance 地獄循環(huán)

希望這篇文章, 能幫你 少熬一次夜

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

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

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