RabbitMQ 发送方确认机制
消费者有确认,生产者同样需要确认:消息发出去后,broker 到底收没收到?Publisher Confirm(发送方确认)让生产者在消息被 broker 正确接收后得到通知,是可靠投递链路的第一环。
confirm 机制原理
生产者先把信道切到确认模式(confirm.select),之后该信道上每一条消息 broker 都会回一个结果:正常时回 Basic.Ack(带自增的 delivery tag),无法处理时回 Basic.Nack 或直接断开连接。生产端据此判断"这条要不要重发"。
Java 客户端开启并同步等待确认:
// 以下片段需放进使用 RabbitMQ Java 客户端的方法中运行
Channel ch = conn.createChannel();
ch.confirmSelect(); // 开启发送方确认
ch.basicPublish("", "q.demo", null, "hello".getBytes());
if (ch.waitForConfirms(5000)) { // 同步等待最多 5 秒
System.out.println("发送成功"); // 输出:发送成功
} else {
System.out.println("收到 nack,需要重发"); // 输出:收到 nack,需要重发
}
waitForConfirms 会阻塞到该信道所有待确认消息出结果:全部 ack 返回 true,出现 nack 返回 false,超时或连接断开抛异常。异常时消息状态未知,重发前要做好幂等防重复。
异步 confirm(高吞吐推荐)
逐条同步等待只适合消息量小的场景。吞吐要求高时应注册监听器,由 broker 异步回调:
// 以下片段需放进使用 RabbitMQ Java 客户端的方法中运行
ch.addConfirmListener(
(tag, multiple) -> System.out.println("已确认: " + tag), // ack 回调
(tag, multiple) -> System.out.println("未确认: " + tag)); // nack 回调
ack 的 delivery tag 是单调递增的序号,配合 multiple 参数可以判断"这条之前是否已全部确认",用于设计批量重发窗口。
与事务(txSelect)对比
RabbitMQ 也支持 AMQP 事务:txSelect 开启、txCommit 提交、txRollback 回滚,同样能保证消息被接收,但每次提交都涉及多轮同步往返,吞吐明显低于 confirm:
| 方案 | 保证 | 性能 | 推荐度 |
|---|---|---|---|
| 事务 txSelect/txCommit/txRollback | 强 | 低(多次同步往返) | 不推荐 |
| Publisher Confirm | 强 | 高(可异步批量确认) | 推荐 |
mandatory 与 returned message
confirm 只能证明 broker"接收"了消息,证明不了"路由到了队列"。若发布时设 mandatory=true 而消息不可路由(交换机没有匹配绑定),broker 会通过 basic.return 把消息退回生产者,生产端要在 return 回调里记录或补偿:
// 以下片段需放进使用 RabbitMQ Java 客户端的方法中运行
ch.addReturnListener((replyCode, replyText, exchange, routingKey,
props, body) -> System.out.println("消息被退回: " + new String(body)));
不带 mandatory 的不可路由消息会被 broker 静默丢弃。因此"不丢消息"要同时做三件事:mandatory 捕获不可路由、confirm 捕获未接收、队列与消息持久化防宕机。
可靠投递全链路清单
一条消息从生产到消费,可靠投递由三层确认拼接而成:
| 环节 | 手段 | 防止的问题 |
|---|---|---|
| 生产者 → broker | confirm(+mandatory) | 发送失败、不可路由 |
| broker 存储 | 队列 durable + 消息持久化 | 节点宕机丢消息 |
| broker → 消费者 | 手动 ack | 处理失败丢消息 |
任一环节缺位,整条链路都可能"看似成功实则丢失"。
小结
Publisher Confirm 是生产端可靠投递的基石:confirmSelect 开启后按 ack/nack 判断发送结果,高吞吐用异步监听;它比事务性能好,配合 mandatory、持久化与消费者手动 ack,才能拼出全链路不丢。