要实现消息百分百送达交换机,需启用publisher confirms并注册ConfirmCallback:先调用channel.confirmSelect()开启确认模式,再通过addConfirmListener()监听ack(成功抵达交换机)与nack(路由失败),注意该机制仅保障到交换机,不保证入队或消费。

要在 Java 中通过 RabbitMQ 发送端开启 ConfirmListener 实现消息百分百送达交换机,核心是启用 publisher confirms 并注册 ConfirmCallback 监听器。这不保证消息被消费,但能确保消息成功到达 RabbitMQ 服务端的交换机(即被 Broker 接收并完成路由判断)。
1. 开启 Channel 的 confirm 模式
在获取 Channel 后,必须显式调用 channel.confirmSelect(),否则 ConfirmCallback 不会触发。这是启用确认机制的前提,否则即使加了监听器也无效。
- 该方法需在发送消息前调用,且每个 Channel 只需调用一次
- 注意:它会使 Channel 进入“confirm 模式”,此时不能再用于事务(transaction)操作
- 若使用 Spring AMQP,对应配置为
spring.rabbitmq.publisher-confirm-type=correlated
2. 注册 ConfirmCallback 监听器
通过 channel.addConfirmListener() 注册回调,监听每条消息是否成功进入交换机。RabbitMQ 在消息成功路由到交换机后(无论是否有队列绑定),会异步返回 ack;如果交换机不存在或路由失败(如 no-route),则返回 nack。
- ack:消息已成功抵达交换机(不等于投递到队列)
- nack:消息被交换机拒绝(例如 exchange 不存在、routing key 无匹配队列且 mandatory=false)
- 回调中的
deliveryTag是唯一标识,可用于关联原始消息(需自行维护映射)
3. 发送消息并关联业务逻辑
发送时建议使用 channel.basicPublish(...) 的标准方式,并配合 deliveryMode=2(持久化)提升可靠性。注意 ConfirmCallback 是异步的,不能直接阻塞等待结果,需结合本地缓存 + 定时重发或状态标记来处理 nack 场景。
- 可将消息 ID(如 UUID)作为
messageId放入消息属性,再在 ack/nack 回调中根据 deliveryTag 查找对应消息做后续处理 - 对 nack 消息,应记录日志、告警,并考虑重新发送或转入死信流程
- 避免在回调里执行耗时操作(如 DB 写入),建议投递到本地队列由单独线程处理
4. 注意事项与常见误区
Confirm 机制只覆盖“消息到达交换机”这一环节,不涉及队列是否存在、消费者是否在线、消息是否被签收等后续阶段。要实现端到端可靠投递,还需配合 mandatory 标志、备份交换机、消费者手动 ack、死信队列等机制。
- 未调用
confirmSelect()→ ConfirmCallback 完全不生效 - 误把 ack 当作“消息已入队”或“已被消费”→ 语义理解错误
- 大量消息快速发送时,deliveryTag 是单调递增 long 值,不是消息序号,需自己维护映射关系
- Channel 关闭或连接中断时,未确认的消息不会自动重发,需应用层兜底
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











