Skip to content

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,失败重回队列),再配合消费幂等,链路就完整了。

基于 VitePress 重建