如何保证消息可靠投递?生产端 confirm 与事务机制怎么选?
结论先行:消息可靠投递要分三段保障:生产端到 Broker、Broker 端存储、Broker 到消费端。生产端主要靠 confirm 确认机制或事务机制,消费端靠手动 ack 与幂等。可靠投递只解决"不丢",不解决"不重",因此最终都要配合消费幂等与对账补偿兜底。
一、三段可靠性模型
| 阶段 | 丢失原因 | 保障手段 |
|---|
| 生产 → Broker | 网络异常、Broker 拒收 | confirm/ack 确认、事务、失败重试 |
| Broker 存储 | 宕机丢内存数据 | 持久化、主从/多副本 |
| Broker → 消费 | 消费者宕机未 ack | 手动 ack、重投、死信兜底 |
二、生产端两种机制对比(RabbitMQ 为例)
| 机制 | 原理 | 优缺点 |
|---|
| confirm(发布确认) | 消息到达交换机/队列后异步回调 ack/nack | 异步、吞吐高,推荐 |
| 事务(txSelect) | 每条消息提交事务,同步等结果 | 吞吐极低,生产基本不用 |
| 本地消息表 | 业务与消息同库事务,定时任务补投 | 与具体 MQ 无关,通用兜底 |
三、常见做法示例
# RabbitMQ 生产者开启发布确认(channel 级)
channel.confirmSelect();
channel.basicPublish("", "q.order", null, body);
if (!channel.waitForConfirms()) {
// 未确认:记录日志,走重试或补偿
}
- 补充:Kafka 生产端对应 acks=all + retries + 幂等生产者,保证发送不丢且不因重试乱序;
- 最终兜底:核心消息在业务库建消息表,与业务操作同一本地事务写入,后台任务扫描未确认消息重新投递;
- 监控指标:关注发送失败率、confirm 超时数、消费堆积量与重投次数,异常要早于用户投诉被发现;
- 对账口径:以业务单据为核心定期核对 MQ 侧与业务侧的完成状态,发现缺口补投消息。
常见追问 / 记忆点
- 追问:confirm 与事务能同时用吗?答:不能,二者互斥,confirm 是异步机制性能远优于事务。
- 追问:可靠投递等于不重复吗?答:不等于,投递保证通常是至少一次,重复消费要靠幂等设计解决。
- 追问:消息一直投递失败怎么办?答:超过重试次数进入死信队列或告警,由人工/补偿任务处理。
- 记忆点:生产 confirm、存储持久化、消费手动 ack,三管齐下再补幂等与对账。