RabbitMQ中重试机制的坑

当我们消息消费失败的时候,可以进行重试,虽然SpringAMQP集成了retry机制,但是发现使用过程有点坑,等会细说
重试机制使用场景:
1.如果是业务代码,比如空指针之类的异常那重试机制其实没什么用
2.如果是调用第三方系统,网络抖动之类的那重试机制就有用

配置使用重试机制

spring.rabbitmq.listener.simple.retry.enabled=true 是否开启消费者重试(为false时重试不生效)
spring.rabbitmq.listener.simple.retry.max-attempts=3  最大重试次数(包含本身消费的1次)
spring.rabbitmq.listener.simple.retry.initial-interval=5000 重试间隔时间(单位毫秒)
spring.rabbitmq.listener.simple.default-requeue-rejected=false 重试次数超过上面的设置之后是否丢弃(false不丢弃时需要写相应代码将该消息加入死信队列)

坑的地方来了:
不管消息被消费了之后是手动确认还是自动确认,代码中不能使用try/catch捕获异常,否则重试机制失效
所以这里要使用重试机制就有2种情况了:
1.如果消息是自动确认,由于异常,多次重试还是失败,消息被自动确认,那消息就丢失了
2.如果消息是手动确认,由于异常,多次重试还是失败,消息没被确认,也无法nack,就一直是unacked状态,导致消息积压

第一种情况代码:

@RabbitListener(queues = "order-queue")
    public void processOrder(Message message, Channel channel, Map map) throws Exception {
            System.out.println("收到订单队列消息:" + message.toString());
            //产生异常,导致订单处理失败
            throw new Exception();
    }

可以看到重试了3次,但是最终还是失败了,消息被自动确认了,丢失了,如果是涉及到金钱之类的就惨了。
在这里插入图片描述
第二种情况代码:

@RabbitListener(queues = "order-queue")
    public void processOrder(Message message, Channel channel, Map map) throws Exception {
            System.out.println("收到订单队列消息:" + message.toString());
            //产生异常,导致订单处理失败
            int a = 1/0;
            //确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

同样是重试三次之后,还是失败
在这里插入图片描述
这里可以看到消息会一直是unacked的状态,因为异常无法ack,然后代码中也无法nack
在这里插入图片描述

如何既保证重试又能不丢失消息呢?这只是我的构思:
首先是手动确认,然后在catch中throw异常触发重试机制,然后定义一个局部变量错误次数retryCount,失败后++,当retryCount=重试次数,则nack并且requeue=false到死信队列中进行入库操作,方便后续人工补偿。

//重试次数
    private int retryCount = 0;

    @RabbitListener(queues = "dead-queue")
    public void process(Message message, Channel channel, Map map) throws IOException {
        System.out.println("收到进入死信队列消息:" + message.toString());
        System.out.println("消息入库,后续补偿");
        //确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
    }

    @RabbitListener(queues = "order-queue")
    public void processOrder(Message message, Channel channel, Map map) throws Exception {
        try {
            System.out.println("收到订单队列消息:" + message.toString());
            int a = 1 / 0;
            //确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            retryCount++;
            //重新抛出异常  触发重试机制
            throw e;
        } finally {
            //重试次数达到限制
            if (retryCount == 3) {
                System.out.println("处理订单消息异常,nack消息到死信队列");
                //不重新入队,发送到死信队列
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            }
        }
    }

可以看到重试3次之后,进入死信队列中。
在这里插入图片描述

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页