沉默是金,总会发光
大家好,我是沉默
快到年底了,系统开始频繁出问题。
我有正当理由怀疑:
老板不想发年终奖,所以开始搞事。
这不,几年都遇不到一次的 Kafka 消息积压,
在一个本该安静下班的夜晚,卷土重来了。
今晚注定是个不眠夜。
原神启动之前,我先启动了 Kafka 面板。
**-**01-
事故现场
事情是这样的。
我刚下班,正准备洗洗睡,
组里的小伙伴突然火急火燎地冲过来:
“Kafka 消息积压一直在涨,预览图全出不来!”
我上去一看:
-
原来的 4 个分区:
已积压 1200+ 条 -
新加的分区:
也开始积压,而且速度越来越快
第一反应很自然:
是不是消费者处理太慢?那我多加几个实例不就完了?
于是:
-
加 Pod
-
消费能跑
-
然后……越跑越卡
-
再然后……Pod 开始挂
这时,我的困意和不祥预感同时达到了顶峰。
- 02-
第一层误判
我突然想起一件事:
Spring Cloud Stream 好像支持并发消费?
于是让开发老哥把 concurrency 改成 10。
结果呢?
-
消息 积压更快
-
Pod 直接被打爆
-
CPU、内存一起飙
这时候才反应过来:
concurrency ≠ 并行处理一条消息
而是:
concurrency = 消费者线程数- 一个线程 = 负责一个分区
- 分区本来就不均匀
- 一加线程,流量倾斜直接拉满
**
**
- 03-
诡异现象
我把所有 Pod 日志拉下来,一条条看。
结果非常魔幻:
-
监听器日志:
全部执行成功 -
但同时又出现:
消费超时 -
Kafka 面板里:
Consumer Group 频繁 Rebalance
我当场愣住。
成功了,又超时?
这是什么薛定谔的消费?
但作为一个坚定的唯物主义者,我选择继续查。
**-****04-**破案关键
问题的答案,藏在 Kafka 的消费模型里。
你以为的 Kafka:
来一条 → 消费一条 → 确认一条
实际上的 Kafka:
消费者主动一次拉一批 → 处理完 → 才提交 offset
而 Spring Cloud Stream,为了“好用”,干了件非常容易坑人类的事:
批量拉取,但监听器只给你一条
假设:
-
max.poll.records = 500 -
每条消息处理 10s
-
处理方式是 串行
-
消费超时时间:
300s
那会发生什么?
500 × 10s = 5000s
一次 poll,最多只能处理 30 条
于是就出现了诡异现象:
-
单条逻辑:成功
-
整批消费:超时
-
Kafka 认为你“失联”
-
触发 Consumer Rebalance
-
offset 不提交
-
后面的消息全堵死
我咧个豆。
案子破了。
**-****05-**两种解决方案
方案一:立刻止血(适合半夜)
ack-mode: RECORD
效果:
- 每条消息处理完立刻提交
- 不再被批次拖死
- 改一行就能下班睡觉
代价:
- 吞吐量下降
- Kafka 的优势用不满
适合:救火、保命、保年终奖
方案二:批量 + 并行(推荐)
思路只有一句话:
批量要小,并行要真
1. 控制批量大小
max.poll.records: 50
2. 自己并行处理这批消息
@StreamListener("<TOPIC>")public void consume(List<byte[]> payloads) { List<CompletableFuture<Void>> futures = payloads.stream().map(bytes -> { Payload payload = JacksonSnakeCaseUtils.parseJson( new String(bytes), Payload.class ); return CompletableFuture.runAsync(() -> { // 业务处理 }, batchConsumeExecutor).exceptionally(e -> { log.error("Thread error {}", bytes, e); return null; }); }).collect(Collectors.toList()); // 等待整批完成,再统一提交 offset CompletableFuture.allOf( futures.toArray(new CompletableFuture[0]) ).join();}
效果:
-
批次不大,不超时
-
真正并行,吞吐拉满
-
offset 提交稳定
-
Kafka 安静了,世界也安静了
**-****06-**总结
这次事故,真正教会我的三件事
1️⃣ Kafka 慢,80% 不是 Kafka 的锅
是你 消费模型 + 超时配置 + 批量大小 没想清楚
2️⃣ Spring Cloud Stream 很友好
但越是“像队列”的封装,越容易误导你
3️⃣ 半夜事故,拼的不是手速
而是你对底层机制的理解深度
程序员的深夜,不该白熬
那天问题解决的时候,已经快天亮了。
咖啡喝完了,
Kafka 面板绿了,
飞书安静了,
我终于能安心睡觉了。
如果你也遇到过:
- Kafka 积压
- 日志成功但超时
- Consumer Rebalance 地狱循环
**
**
希望这篇文章,能帮你少熬一次夜。
**-****07-**粉丝福利
我这里创建一个程序员成长&副业交流群,
和一群志同道合的小伙伴,一起聚焦自身发展,
可以聊:
技术成长与职业规划,分享路线图、面试经验和效率工具,
探讨多种副业变现路径,从写作课程到私活接单,
主题活动、打卡挑战和项目组队,让志同道合的伙伴互帮互助、共同进步。
如果你对这个特别的群,感兴趣的,
可以加一下, 微信通过后会拉你入群,
但是任何人在群里打任何广告,都会被我T掉。