【RabbitMQ】06-消费者的可靠性


1. 消费者确认机制

没有ack,mq就会一直保留消息。

java 复制代码
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: auto # 自动ack

2. 失败重试机制

当消费者出现异常后,消息会不断requeue(重入队)到队列,再重新发送给消费者。如果消费者再次执行依然出错,消息会再次requeue到队列,再次投递,直到消息处理成功为止。

极端情况就是消费者一直无法执行成功,那么消息requeue就会无限循环,导致mq的消息处理飙升,带来不必要的压力。

java 复制代码
spring:
  rabbitmq:
    listener:
      simple:
        retry:
          enabled: true # 开启消费者失败重试
          initial-interval: 1000ms # 初识的失败等待时长为1秒
          multiplier: 1 # 失败的等待时长倍数,下次等待时长 = multiplier * last-interval
          max-attempts: 3 # 最大重试次数
          stateless: true # true无状态;false有状态。如果业务中包含事务,这里改为false

重启consumer服务,重复之前的测试。可以发现:

  • 消费者在失败后消息没有重新回到MQ无限重新投递,而是在本地重试了3次
  • 本地重试3次以后,抛出了AmqpRejectAndDontRequeueException异常。查看RabbitMQ控制台,发现消息被删除了,说明最后SpringAMQP返回的是reject

结论:

  • 开启本地重试时,消息处理过程中抛出异常,不会requeue到队列,而是在消费者本地重试
  • 重试达到最大次数后,Spring会返回reject,消息会被丢弃

3. 失败处理策略

在之前的测试中,本地测试达到最大重试次数后,消息会被丢弃。这在某些对于消息可靠性要求较高的业务场景下,显然不太合适了。

因此Spring允许我们自定义重试次数耗尽后的消息处理策略,这个策略是由MessageRecovery接口来定义的,它有3个不同实现:

  • RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息。默认就是这种方式
  • ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队
  • RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机

比较优雅的一种处理方案是RepublishMessageRecoverer,失败后将消息投递到一个指定的,专门存放异常消息的队列,后续由人工集中处理。

代码:

java 复制代码
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(name = "spring.rabbitmq.listener.simple.retry.enabled", havingValue = "true")
public class ErrorMessageConfiguration {
//    @RabbitListener(bindings = @QueueBinding(
//            value = @Queue(name = "error.queue"),
//            exchange = @Exchange(name = "error.direct", type = ExchangeTypes.DIRECT),
//            key = {"error"}
//    ))
//    public void bings(Object msg){
//        System.out.println("异常"+msg.toString());
//    }
    @Bean
    public DirectExchange errorMessageExchange(){
        return new DirectExchange("error.direct");
    }
    @Bean
    public Queue errorQueue(){
        return new Queue("error.queue", true);
    }
    @Bean
    public Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){
        return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with("error");
    }
    @Bean
    public MessageRecoverer messageRecoverer(RabbitTemplate rabbitTemplate) {
        return new RepublishMessageRecoverer(rabbitTemplate, "error.direct", "error");
    }
}
相关推荐
装不满的克莱因瓶9 小时前
支付订单成功异步MQ权益发放设计和开发
java·架构·系统架构·rabbitmq·硬件架构
Thanks_ks11 小时前
Cookie 和 Session 到底是什么关系——登录态是怎么维持的
分布式·http·jwt·cookie·session·后端开发·登录态
千里达景区票务系统12 小时前
消息队列选型对比:RabbitMQ vs RocketMQ vs Kafka
kafka·消息队列·rabbitmq·rocketmq
Sayai13 小时前
Kafka 跨数据中心日志同步实战:MirrorMaker 2 选型对比 + Flink 中继任务落地(双中心互备与消费者组隔离避坑)
大数据·运维·分布式·flink·kafka·消息队列·高可用
朋友圈自动点赞工具14 小时前
Ruby方法查找:对象不存方法
开发语言·前端·ruby·科技资讯
CodeGL1 天前
开源的分布式任务调度平台:XXL-Job
分布式·开源
一条泥憨鱼2 天前
【Kafka】零基础学习教程
java·分布式·学习·kafka
贾伟康2 天前
【共创稿事节】HarmonyOS 7.0 HiTraceChain分布式调用链:从接入到生产验证
分布式·harmonyos·可观测性·调用链·hitracechain
NiceZack3 天前
Hadoop 部署全流程教程:从新装 CentOS 到伪分布式与完全分布式
hadoop·分布式·centos
傻啦嘿哟3 天前
分布式爬虫架构:Redis+Scrapy打造企业级爬虫系统
分布式·爬虫·架构