【记录】RabbitMQ消息消费失败后的处理方案

4,097 阅读3分钟
  • Q:业务系统之间通过MQ进行交互,当消费者发生未知异常时,消息消费失败,如何处理才保证消息的消费的可靠性。

  • A:

    • 从如下几点考虑

      • 何时ack

        • 无论消息成功还是失败,都会ack,消息不会堆积在MQ中

        • 只有成功才ack,消息堆积在MQ中

      • 消费日志

        • 接收消息后先入库

          • 入库失败,直接开始重试

          • 入库成功,开始后续业务逻辑

        • 消息id,不能重复入库,可以更新状态,比如重试次数,执行状态

        • 记录从哪儿来的

          • 队列名

          • 交换器

          • 路由键

          • 消息数据

        • 状态

          • 重试状态
      • 消费失败重试

        • 原则:

          • 可以重新消费消息

          • 可定制重试间隔时间与重试次数

          • 集群环境下的重试,如果本节点出问题时,可由其他节点消费,尽快完成业务逻辑

          • 重试状态记录

          • 达到重试上限(时间、次数)后,变更数据,并酌情通知人工干预

        • 可选方案

          • rabbitmq 自带重试机制探究

          • 利用死信机制实现重试

            • 服务宕机,或者断网时,无法继续重试,只能入库后等待补偿
      • 补偿

        • 定时任务

        • 根据最终失败状态的消息,进行补偿,补偿逻辑需要业务方实现。

  • rabbitmq 自带重试机制

    • spring-rabbitmq,重试机制也是在当前系统内存中重试。

      • 本质上使用的是spring的retry机制
    • 可实现功能

      • 定制重试间隔机制

        • 间隔时间

        • 间隔时间幂因子

        • 最大时间间隔

        • 最大重试次数

      • 重试过程监听

        • 重试开始前回调

        • 每次重试失败时回调

        • 达到最大重试次数,结束重试时回调

      • 重试结束时,消息处理策略MessageRecoverer

        • ImmediateRequeueMessageRecoverer,

          • 立即重新返回队列

          • 如果存在其他消费者,此消息可被其他消费者重新消费

          • 消息会一直重试下去,需要自己处理,跳出重试循环

        • RejectAndDontRequeueRecoverer

          • 拒绝消息,并且不会重新回到队列

          • 消息会被投递到死信交换器/死信队列,如果当前队列有相关配置

          • 仅仅在本节点中重试,直到失败

        • RepublishMessageRecoverer

          • 重新发布出去,且消息头中会携带异常堆栈信息,x-exception

          • 可以指定exchange,如果不指定将使用一些默认值,

          • 可以考虑异步处理失败消息。

      • 重试结束时,如何更新DB中消息的状态

        • 重试中消费成功,可在消费者method中处理,直接更新消息状态

        • 结束时,消费失败

          • 会触发retry 结束监听时间,
            • 但是这个监听器拿不到消息参数
        • 异常处理errorHandler

          • 此处理器,每次失败都会触发,可以拿到消息的信息

            • 可以考虑每次更新DB中消息的重试次数,或者状态

            • 重试次数在数据库中累加,

            • 但是会增加DB压力