Skip to content

保证消息不丢失

一、RabbitMQ的典型应用场景

  • 异步发送:验证码、短信、邮件通知等非核心业务异步执行
  • 数据同步:MySQL与Redis、Elasticsearch之间的数据同步
  • 分布式事务:实现跨服务的事务一致性保障
  • 削峰填谷:应对高并发场景下的流量冲击,保护后端服务
  • 其他:延迟任务、日志处理、解耦系统等场景

二、消息丢失的核心场景

一条消息从生产者发送到消费者处理完成,可能在三个环节丢失:

  1. 生产者发送阶段:消息未到达交换机(网络波动、服务宕机)
  2. MQ存储阶段:消息到达交换机/队列后,MQ宕机未持久化
  3. 消费者处理阶段:消费者接收消息后,未处理完成就宕机,消息被误删除

三、生产者确认机制(解决发送阶段丢失)

RabbitMQ 提供 publisher confirm 机制,确保消息成功到达 MQ 服务端:

  • 消息发送到 MQ 后,会返回 ack(成功)或 nack(失败)结果给生产者
  • 失败的消息可通过以下方式处理:
    • 回调方法即时重发
    • 记录日志用于问题排查
    • 保存到数据库,通过定时任务重试

四、消息持久化(解决MQ存储阶段丢失)

MQ 默认内存存储消息,开启持久化可确保服务重启后消息不丢失,分为三层:

  1. 交换机持久化

    java
    @Bean
    public DirectExchange simpleExchange() {
        // 参数:名称、是否持久化、无队列绑定时是否自动删除
        return new DirectExchange("simple.direct", true, false);
    }
  2. 队列持久化

    java
    @Bean
    public Queue simpleQueue() {
        // durable=true 表示队列持久化
        return QueueBuilder.durable("simple.queue").build();
    }
  3. 消息持久化 SpringAMQP 中消息默认持久,可通过 MessageProperties 指定投递模式:

    java
    Message msg = MessageBuilder
        .withBody(message.getBytes(StandardCharsets.UTF_8))
        .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
        .build();

五、消费者确认机制(解决消费阶段丢失)

RabbitMQ 支持消费者确认机制:消费者处理消息后发送 ack 回执,MQ 收到后才删除消息。SpringAMQP 支持三种模式:

  • manual:手动 ack,业务代码执行完成后调用 API 发送确认
  • auto:自动 ack,Spring 监测 listener 代码无异常则返回 ack,抛出异常则返回 nack
  • none:关闭 ack,消息投递后立即删除,不保证消费成功

可结合 Spring Retry 机制实现重试兜底:

  • 配置本地重试次数,消费失败时自动重试
  • 重试耗尽后,将消息投递到死信交换机/死信队列,交由人工处理

六、面试相关问题

  1. 问:RabbitMQ 中消息丢失的场景有哪些?如何解决?答: 消息丢失主要有三个场景及对应解决方案:

    • 生产者发送阶段:消息未到达 MQ,可通过 publisher confirm 机制实现发送确认,失败消息重发或落库重试;
    • MQ 存储阶段:MQ 宕机导致内存消息丢失,需开启交换机、队列、消息三级持久化;
    • 消费者处理阶段:消费者接收消息后宕机,可通过手动 ack 机制,业务处理成功后再确认,配合重试和死信队列兜底。
  2. 问:RabbitMQ 中生产者确认机制的作用是什么?答: 生产者确认机制(publisher confirm)用于解决消息发送过程中的丢失问题。消息发送到 MQ 后,会返回 ack/nack 结果给生产者,生产者可据此判断消息是否成功到达。失败的消息可通过即时重发、日志记录或数据库定时重试的方式处理,确保消息不丢失。

  3. 问:RabbitMQ 中消费者确认的三种模式有什么区别?答: 三种模式区别如下:

    • manual:手动 ack,需业务代码主动调用 API 发送确认,灵活性高,能精准控制消息处理状态;
    • auto:自动 ack,由 Spring 监测 listener 代码是否异常,无异常则 ack,异常则 nack;
    • none:关闭 ack,消息投递后立即删除,不保证消费成功,仅适用于可丢失的日志类场景。
  4. 问:消息持久化是否一定能保证消息不丢失?答: 不一定。持久化能解决 MQ 宕机导致的消息丢失,但如果消息还未写入磁盘就宕机,仍可能丢失。因此需结合生产者确认机制,确保消息写入磁盘后再返回 ack,同时配合消费者确认机制,确保消费阶段的消息不丢失,三者结合才能最大程度保证消息可靠。

Powered by VitePress 1.6.4 | 持续更新中