深色模式
消息积压排查
摘要:消息积压只有两种根因:生产端突然变快,或消费端突然变慢。本文给出一套通用的排查顺序,配合 Kafka 与 RabbitMQ 各自的诊断命令,帮你在几分钟内判断是哪一端出问题,并给出应急扩容与根因定位方法。
适用环境
bash
# Kafka:先确认滞后量
/opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--describe --group <group> 2>/dev/null
# RabbitMQ:确认堆积量与消费者
sudo rabbitmqctl list_queues name messages messages_ready \
messages_unacknowledged consumers -q
# 消费端进程与资源
ps -o pid,pcpu,pmem,etime,cmd -C java | head
top -bn1 | head -151
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
操作步骤
1. 第一步:判断是"生产突增"还是"消费变慢"
bash
# Kafka:对比生产速率与消费速率
curl -s http://127.0.0.1:9404/metrics \
| grep -E 'records_consumed_rate|records_produced_rate' | head1
2
3
2
3
bash
# RabbitMQ:看 published 与 delivered 速率差
curl -s -u appuser:'密码' 'http://127.0.0.1:15672/api/overview?lengths_age=60&lengths_incr=5&rates_age=60&rates_incr=5&rates_mode=detailed' | head -c 8001
2
2
判据:速率上升且消费速率跟上 → 生产突增(可接受,观察即可);生产平稳而 lag 上升 → 消费变慢(需处置)。
2. 第二步:确认消费者是否还活着
bash
# Kafka:组成员是否齐全
/opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--describe --group <group> --members --verbose | head -20
# RabbitMQ:消费者为 0 说明客户端已断开
sudo rabbitmqctl list_queues name consumers -q
sudo rabbitmqctl list_consumers -q | head1
2
3
4
5
6
7
2
3
4
5
6
7
3. 第三步:定位是"卡在哪条消息"还是"整体变慢"
bash
# Kafka:看每个分区的 lag 分布
/opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--describe --group <group> 2>/dev/null | awk 'NR>1{print $2,$6}' | sort -k2 -nr | head1
2
3
2
3
只有个别分区 lag 极高而其他分区正常 → 典型"毒丸消息"或热点 key,消费者反复重试同一条消息卡住。
4. 第四步:抓消费端线程栈(Java 应用)
bash
PID=$(pgrep -f 'YourConsumerApp' | head -1)
jstack $PID > /tmp/jstack-$(date +%H%M%S).txt
# 连续抓 3 次,间隔 5 秒,看同一线程是否停在相同位置
grep -A 5 'BLOCKED\|WAITING' /tmp/jstack-*.txt | head -401
2
3
4
2
3
4
bash
# 线程停在数据库/HTTP 调用处 -> 下游慢
# 线程停在业务计算处 -> 单条处理耗时过长
# 线程大量 WAITING 且无进展 -> 线程池打满或死锁1
2
3
2
3
5. 第五步:应急扩容消费能力
bash
# Kafka:增加消费者实例(数量 <= 分区数)
# RabbitMQ:增加消费者实例,并适当调大 prefetch 之外的并发
# 临时手段:跳过毒丸消息(确认可丢弃后)
/opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--group <group> --topic <topic> --reset-offsets --to-latest --execute1
2
3
4
5
2
3
4
5
DANGER
--reset-offsets --to-latest 会丢弃未消费的消息,属于不可逆操作。执行前必须确认这批消息可丢弃,并保留操作记录;--to-earliest 则会引发全量重放,可能冲垮下游。
6. 第六步:检查下游依赖
bash
# 消费慢往往是下游慢:DB、HTTP 接口、Redis
curl -s -o /dev/null -w '%{time_total}\n' http://下游接口/healthz
# 数据库慢查询
mysql -e "SHOW PROCESSLIST;" | head -201
2
3
4
2
3
4
验证
bash
# 积压是否开始下降(观察 5 分钟趋势)
watch -n 10 '/opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group <group> 2>/dev/null | tail -n +2'
# RabbitMQ 同理
watch -n 10 'sudo rabbitmqctl list_queues name messages_ready -q'
# 消费端 CPU/线程是否恢复正常
top -bn1 -p $PID | tail -21
2
3
4
5
6
7
8
2
3
4
5
6
7
8
常见坑
WARNING
看到积压就扩容消费者,是最常见的无效处置。如果根因是下游数据库慢,扩容只会让数据库压力更大、整体更慢。先定位根因再动手。
WARNING
消费者频繁 rebalance(日志刷 Attempt to heartbeat failed)会导致消费暂停,看起来像"消费慢"。常见原因是 max.poll.interval.ms 设置过短而单批处理耗时过长。
DANGER
把消费失败的消息无限重试而不进死信队列,会让单条消息永久阻塞整个分区(Kafka)或反复重投(RabbitMQ)。必须设置最大重试次数 + 死信队列。