RabbitMQ 高级特性之重试机制

1. 简介

在消息传递时,有可能会因为网络故障、内存资源不足等不可控原因,导致消息没有成功发送出去,这时我们就可以选择将这条消息进行重新发送。能够重新发送的前提是代码逻辑没有问题。

2. 需要的配置

java 复制代码
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: auto
        retry:
          enabled: true # 开启消费者失败重试
          initial-interval: 5000ms # 初始失败等待时⻓为5秒
          max-attempts: 5 # 最⼤重试次数(包括自身消费的⼀次)

在配置信息中设置了消息的重发次数,该次数包括一开始生产者发送消息的那一次。

这里我们需要注意,在使用重试机制时,需要搭配 auto 使用,原因下面进行讲解。

3. auto 与重试机制搭配使用的结果

生产者代码如下:

java 复制代码
@RequestMapping("/producer")
@RestController
public class ProducerController {

    @Resource(name = "rabbitTemplate")
    private RabbitTemplate rabbitTemplate;

    /**
     * 重试机制
     */
    @RequestMapping("/retry")
    public String retry() {
        String message = "retry test...";

        rabbitTemplate.convertAndSend(Constants.RETRY_EXCHANGE, Constants.RETRY_ROUTINGKEY, message);

        return "消息发送成功";
    }
}

对于无异常版本的消费者代码,其代码运行结果即正常消费消息,下面只使用有异常版本的消费者代码:

java 复制代码
@Slf4j
@Component
public class RetryListener {

    @RabbitListener(queues = Constants.RETRY_QUEUE)
    public void listener(Message message) {
        String messageInfo = new String(message.getBody());
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        log.info("接收消息, message: {}, deliveryTag: {}", messageInfo, deliveryTag);

        int num = 1 / 0;

        log.info("消息消费完成");
    }
}

代码运行结果如下:

由于在消费者代码中出现了除数为 0 的错误,于是消息就消费失败,触发了消息重发机制后兄奥西重发了 4 次,消息一共发送了 5 次。并且在日志中可以看出,这些消息的 deliveryTag 都是相同的。

4. manual 与 重试机制搭配使用的结果

将配置文件的 acknowledge-mode 修改为 manual,并在消费者代码中添加 basicAck 和 basicNack,代码如下:

java 复制代码
@Slf4j
@Component
public class RetryListener {

    @RabbitListener(queues = Constants.RETRY_QUEUE)
    public void listener(Message message, Channel channel) throws IOException {

        String messageInfo = new String(message.getBody());
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        try {
            log.info("接收消息, message: {}, deliveryTag: {}", messageInfo, deliveryTag);

            int num = 1 / 0;

            log.info("消息消费完成");

            channel.basicAck(deliveryTag, false);

        } catch (Exception e) { //此处的异常必须包含 除数为 0 的异常,不然就不会进入到 catch 中,就导致不会执行 basicNack 方法,就无法将消息重新入队列
            channel.basicNack(deliveryTag, false, true);
        }
    }
}

代码运行结果如下:

从结果中可以看出,虽然设置了重发次数为5次,但是 deliveryTag 却一直在递增,就表示该消息一直在入队列并发送,没有受到重发机制的限制。

所以在重试机制下,搭配 auto 确认模式使用才能发挥效果。

相关推荐
我命由我123452 小时前
Kotlin 数据容器 - List(List 概述、创建 List、List 核心特性、List 元素访问、List 遍历)
java·开发语言·jvm·windows·java-ee·kotlin·list
liulilittle2 小时前
C++ TAP(基于任务的异步编程模式)
服务器·开发语言·网络·c++·分布式·任务·tap
码字的字节2 小时前
ZooKeeper在Hadoop中的协同应用:从NameNode选主到分布式锁实现
hadoop·分布式·zookeeper·分布式锁
武子康4 小时前
Java-80 深入浅出 RPC Dubbo 动态服务降级:从雪崩防护到配置中心秒级生效
java·分布式·后端·spring·微服务·rpc·dubbo
YuTaoShao7 小时前
【LeetCode 热题 100】131. 分割回文串——回溯
java·算法·leetcode·深度优先
源码_V_saaskw7 小时前
JAVA图文短视频交友+自营商城系统源码支持小程序+Android+IOS+H5
java·微信小程序·小程序·uni-app·音视频·交友
超浪的晨8 小时前
Java UDP 通信详解:从基础到实战,彻底掌握无连接网络编程
java·开发语言·后端·学习·个人开发
双力臂4048 小时前
Spring Boot 单元测试进阶:JUnit5 + Mock测试与切片测试实战及覆盖率报告生成
java·spring boot·后端·单元测试
心之语歌8 小时前
Spring AI MCP 客户端
人工智能·spring·github
Edingbrugh.南空8 小时前
Aerospike与Redis深度对比:从架构到性能的全方位解析
java·开发语言·spring