消息队列消费组积压了八十多万条消息,消费速率死活上不去。加了消费者实例,积压没降,反而因为 rebalance 短暂上升。这事我在 ZEEKEAI(zeekeai.com) 上用 Claude Fable 5.1 配合消费端指标分析了一轮,从消费速率和分区分布里定位到两个分区拖了后腿。
先把消费组的指标导出来。用命令行工具拉取每个分区的消费位点和最新位点,算出积压量:
bash
bash
#!/usr/bin/env bash
# 导出消费组每个分区的积压量
GROUP="order-consumer"
BOOTSTRAP="kafka-broker:9092"
kafka-consumer-groups.sh --bootstrap-server "$BOOTSTRAP" \
--describe --group "$GROUP" 2>/dev/null \
| awk 'NR>1 && $2 != "" {
# 列:GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST
printf "%s\t%s\t%s\t%s\t%s\n", $2, $3, $4, $5, $6
}' | sort -t$'\t' -k5 -rn > lag.tsv
# 按主题汇总积压量
awk -F'\t' '{sum[$1]+=$5} END {for (t in sum) printf "%s\t%d\n", t, sum[t]}' lag.tsv \
| sort -k2 -rn
跑出来积压集中在两个主题上,其中一个主题的积压量占了总量的七成。按分区看,这个主题的十二个分区里,只有两个分区的积压特别高,其他分区的积压是个位数。
把分区积压数据喂给模型,让它按分区分布判断瓶颈在哪:
text
下面是 Kafka 消费组各分区的积压量,按主题分组。
只做一件事:分析积压的分区分布特征,判断瓶颈在消费者整体还是个别分区。
输出格式:主题 | 分区总数 | 高积压分区数 | 积压分布特征 | 初步判断。
不要给解决方案,只给分析。
分析结果很明确:积压集中在两个分区,其他分区消费正常。消费者整体消费速率没问题,是两个分区对应的消费者线程卡住了。
查这两个分区为什么慢。看消费端的日志,这两个分区的消费线程里有大量重复的 WARN 日志,内容是"等待数据库连接"。让模型结合消费逻辑分析:
text
markdown
两个 Kafka 分区的消费线程日志里频繁出现"等待数据库连接",其他分区没有。
消费逻辑是:每条消息解析后写入数据库,单条写入耗时约 10 毫秒。
只回答三个问题:
1. 为什么只有这两个分区的消费线程在等数据库连接?
2. 这两个分区的消息有什么特征?
3. 连接等待和积压的因果关系是什么?
每条给依据,不要推测。
查了这两个分区的消息内容,消息里带的业务类型恰好是需要写大字段的订单,单条写入耗时是普通消息的几十倍。消费线程处理慢,连接被长时间占用,其他线程也在等连接,形成连锁反应。
让模型确认连接池配置和消费并发是否匹配:
text
markdown
消费端线程数 12,数据库连接池最大连接数 10。
这两个分区的消息单条写入耗时是普通消息的几十倍。
只回答两个问题:
1. 线程数和连接池大小是否匹配?
2. 大字段消息占着连接不放,对其他分区的消费有什么影响?
每条给依据,不要推测。
确认了:12 个消费线程抢 10 个连接,本来就不够。大字段消息让连接被占用时间变长,普通消息的消费线程也在排队等连接,整体消费速率被拖慢。
改法分两步。第一步,把大字段消息拆到独立的消费组和连接池,不让它跟普通消息抢资源:
java
typescript
// 按消息类型路由到不同的消费逻辑
@KafkaListener(topics = "order-events", groupId = "order-consumer")
public void onMessage(ConsumerRecord<String, String> record) {
String type = extractType(record.value());
if ("BIG_FIELD".equals(type)) {
// 大字段消息转到独立队列,由独立消费者处理
bigFieldProducer.send("order-events-bigfield", record.value());
return;
}
// 普通消息正常处理
processNormal(record.value());
}
大字段消息的消费组单独配连接池,最大连接数按消费线程数配,互不干扰。
第二步,消费端的线程数和连接池大小对齐:
yaml
yaml
spring:
datasource:
hikari:
maximum-pool-size: 16 # 从 10 加到 16,和消费线程数匹配
connection-timeout: 3000
连接池从 10 加到 16,比消费线程数 12 多留几个余量,避免线程抢连接。
改完观察一天,积压从八十多万降到两万以内,消费速率恢复到正常水平。
这次分析的关键步骤
| 步骤 | 做法 | 效果 |
|---|---|---|
| 导出分区积压 | 用消费组命令按分区拉位点 | 看到积压集中在两个分区 |
| 分析分布特征 | 让模型判断是整体还是局部 | 定位到个别分区拖后腿 |
| 查分区消息特征 | 看消费日志和消息内容 | 发现大字段消息写入慢 |
| 核对资源配置 | 对比线程数和连接池大小 | 确认资源不匹配 |
最后一句
消息积压清不掉,先看积压是均匀分布还是集中在个别分区。均匀分布是消费者整体能力不足,集中在个别分区是那部分消息处理慢。定位到之后,看慢消息是不是占了本该给普通消息的资源,把慢消息隔离到独立消费组,比加消费者实例管用。
你们消息队列积压时是怎么排查的?有没有遇到过个别分区拖后腿的情况?评论区聊聊。