RabbitMQ 消息“偶发丢失”排查与解决方案(千万级场景)
RabbitMQ 消息“偶发丢失”排查与解决方案(千万级场景)

🔍 问题现象
- 手动测试正常,但千万级消息中偶发少量缺失
- 发送端已启用
Publisher Confirm,消费端使用@RabbitListener
🚨 根本原因:队列配置限制
1. x-max-length: 5000000
- 行为:队列超过 500 万条时,自动丢弃最旧消息(head drop)
- 后果:发送 1000 万条 → 最早 500 万条被静默删除 → “看似丢失”
- 验证:发送 5,000,001 条带 ID 消息,第一条必丢
2. x-message-ttl: 2592000(30 天)
- 消息在队列停留 >30 天自动删除(次要原因,除非消费严重滞后)
✅ 解决方案
✅ 首选:移除长度限制(关键!)
// 声明队列时不要设置 max-length 和 TTL
channel.queueDeclare("hex_kyc", true, false, false, null);适用场景:订单、交易等不可丢消息
注意:确保磁盘空间充足
⚠️ 备选:拒绝新消息(不推荐)
Map<String, Object> args = new HashMap<>();
args.put("x-max-length", 5_000_000);
args.put("x-overflow", "reject-publish"); // 满时拒绝新消息
channel.queueDeclare("hex_kyc", true, false, false, args);- 生产者需处理
basic.publish被拒绝(会收到 NACK) - 降低吞吐,增加复杂度
🔧 消费端加固(防阻塞)
异常消息(如解析失败)会导致:
- 无限 requeue → 卡住消费者线程 → 后续消息无法处理(看似丢失)
推荐做法:隔离异常消息
@RabbitListener(queues = "hex_kyc")
public void receiveMessage(String message) {
try {
process(message);
} catch (Exception e) {
log.error("转入死信队列: {}", message, e);
// 发送到错误队列,避免 requeue
rabbitTemplate.convertAndSend("hex_kyc.dlq", message);
// 不抛异常 → Spring 自动 ACK 主队列消息
}
}或配置 RabbitMQ 死信队列(DLQ) + 修复解析逻辑(如 ArrayIndexOutOfBoundsException)。
📊 快速自查清单
| 检查项 | 命令/方法 | |
|---|---|---|
| 队列是否受限 | rabbitmqctl list_queues name arguments | |
| 当前消息数 | rabbitmqctl list_queues name messages | |
| 是否有 DLQ | 查看是否存在 hex_kyc.dlq 队列 | |
| 消费者是否卡住 | `jstack | grep RabbitMqListener` |
💡 总结
“千万条漏几条”的主因是
x-max-length静默丢弃旧消息。
解决步骤:
- 移除
x-max-length- 修复消费端异常处理(防阻塞)
- 添加唯一 ID + 对账机制(可选)
很好