P92 RabbitMQ 如何确保消息发送?消息接收?
面试题:RabbitMQ 怎么保证消息不丢失?(生产端、服务端、消费端全链路)
1. 消息丢失的三个环节
text
① 生产端:消息发出去但没到 Broker
② 服务端:Broker 收到但宕机/重启丢了(内存中)
③ 消费端:消费者收到但没处理完就挂了2. 生产端:确保消息发到 Broker
① 发布确认(Publisher Confirm)
java
channel.confirmSelect();
// 发送消息后等待 ack
if (channel.waitForConfirms()) {
// 发送成功
} else {
// 失败重发
}- Broker 持久化成功后返回 ack;
- 批量 confirm / 异步 confirm 提升性能;
- 失败要重试 + 落库(发消息前先存本地消息表,对账补偿)。
② Mandatory + Return
消息路由不到队列时,通过 Return 回调通知生产者,避免消息静默丢失。
3. 服务端:确保 Broker 不丢
① 队列持久化
java
boolean durable = true;
channel.queueDeclare("order_queue", durable, false, false, null);② 消息持久化
java
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.deliveryMode(2) // 2 = 持久化消息
.build();③ 交换机持久化
交换机也设置 durable,否则重启后路由规则丢失。
注意:队列、交换机、消息三处都要持久化才真正不丢。
4. 消费端:确保消息被处理
① 手动 ACK(关键)
java
channel.basicConsume(queue, false, (tag, delivery) -> {
try {
handle(delivery); // 业务处理
channel.basicAck(tag, false); // 成功才 ack
} catch (Exception e) {
channel.basicNack(tag, false, true); // 失败重回队列(或进死信)
}
});- 关闭自动 ack(
autoAck=false); - 处理成功再 ack,处理失败 nack/requeue;
- 消费者挂了没 ack,消息会重新投递给其他消费者(至少一次投递)。
5. 消息幂等
因为 RabbitMQ 是"至少一次"投递,消费端必须幂等:
- 消费记录表/唯一键去重;
- 或 Redis SETNX 判重。
6. 加分点
- 完整链路答法:"生产 Confirm + 持久化 + 手动 ACK + 幂等消费";
- 提到事务消息(channel.txSelect)性能差,用 Confirm 替代;
- 追问"重复消费怎么办":幂等(业务幂等键)。
一句话总结
确保不丢消息:生产端 Confirm 确认 + 失败重试,服务端 队列/交换机/消息三处持久化,消费端 手动 ACK(成功才 ack,失败重回队列),再配合消费幂等,链路就完整了。