RabbitMQ 消息可靠性:publisher confirm、持久化与手动 ack
消息发出去不代表到了。本文梳理 RabbitMQ 消息丢失的三个环节(发布端、Broker、消费端),并给出 publisher confirm、持久化和手动 ack 的完整可靠性方案。
一、问题案例
订单服务通过 RabbitMQ 发「下单成功」消息给下游,偶尔下游说没收到,订单状态对不上。查日志,发布端明明「发送成功」了,消费者却没消费。
java
rabbitTemplate.convertAndSend("order.exchange", "order.create", msg);
// 返回了,就以为发出去了
二、原理详解:消息丢失的三个环节
消息丢失可能发生在三个环节:
- 发布端 → Broker :没开 publisher confirm,
convertAndSend只是「发出去就返回」,不保证 Broker 真的收到。exchange 名字写错、路由不到队列,消息直接丢,发布端无感。 - Broker 内部:exchange / 队列没有持久化,Broker 重启后消息丢失。
- Broker → 消费者:没有手动 ack,消费者拿到消息就丢;或者 ack 策略不对,导致重复投递混乱。
三、实战代码:可靠性三件套
发布端开 confirm + return:
yaml
spring.rabbitmq.publisher-confirm-type: correlated
spring.rabbitmq.publisher-returns: true
队列和消息持久化:
java
// 声明持久化队列
new Queue("order.queue", true);
// 发送时消息持久化
Message message = MessageBuilder.withBody(payload)
.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
.build();
消费者手动 ack:
yaml
spring.rabbitmq.listener.simple.acknowledge-mode: manual
java
@RabbitListener(queues = "order.queue")
public void onMessage(Message message, Channel channel) throws IOException {
try {
// 处理业务
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}
四、常见踩坑
- confirm 后仍可能重复投递:确认只保证「Broker 收到了」,不保证「只投递一次」,消费端要做幂等。
- 可靠性是组合拳:发送确认 + 持久化 + 手动 ack + 幂等,缺一环就可能丢消息或重复消费。
五、总结
- 消息丢失有三环:发布端、Broker、消费端,都要防。
- confirm + 持久化 + 手动 ack,三件套缺一不可。
- 消费端幂等,兜底重复投递。
我是无羡(小剑),全栈偏后端的独立开发者。
作品集:无羡 · 独立开发者作品集
如果对你有帮助,欢迎点赞、收藏、关注。