前言
在消息传递过程中,可能会遇到各种问题,如网络故障,服务不可用,资源不足等,这些问题可能导致消息处理失败.为了解决这些问题,RabbitMQ提供了重试机制,允许消息在处理失败后重新发送.
但如果是程序逻辑引起的错误,那么多次重试也是没有用的,如果重复入队反复消费这是不可取的,因此我们可以设置重试次数
配置信息
我们先将 确认模式调成默认的 (auto)
java
spring:
rabbitmq:
addresses: xxxx
listener:
simple:
acknowledge-mode: auto # 设置确认模式
retry:
enabled: true # 开启消费者失败重试
initial-interval: 5000ms # 初始失败等待时长为5秒
max-attempts: 5 # 最大重试次数 包括(包括自身消费的⼀次)
initial-interval 和 max-attempts 大家可以自行发挥
这里的最大重试次数包括自身消费的一次,是指,第一次发送给消费者也算进重试次数
代码演示
AUTO 模式
常量类:
java
//消息退回
public static final String RETRY_QUEUE = "RETRY_QUEUE";
声明队列:
java
//重试机制
@Bean("retryQueue")
public Queue retryQueue() {
return QueueBuilder.durable(MQConstants.RETRY_QUEUE).build();
}
生产者:
java
@RequestMapping("/retry")
public String retry() {
rabbitTemplate.convertAndSend("", MQConstants.RETRY_QUEUE, "retry");
return "消息发送成功";
}
消费者:
java
@Component
@RabbitListener(queues = "RETRY_QUEUE")
public class RetryListener {
@RabbitHandler
public void handle(String messageContent, Channel channel, Message message) throws IOException {
System.out.println("接收到的消息:" + messageContent);
int n = 10 / 0; //添加异常,观察重试
System.out.println("消息消费成功");
}
}
运行程序观察:
在AUTO模式下,重试次数达到指定次数之后,就会将消息丢弃
NONE 模式
配置信息:
java
spring:
application:
name: abbitExtensionDemo
rabbitmq:
addresses: xxxx
listener:
simple:
acknowledge-mode: none # 设置确认模式
retry:
enabled: true # 开启消费者失败重试
initial-interval: 5000ms # 初始失败等待时长为5秒
max-attempts: 5 # 最大重试次数
NONE 和 AUTO 模式一样,消息达到重试次数之后,就会自动丢弃
自动确认模式下,RabbitMQ会在消息被投递给消费者后自动确认消息,如果消费者处理消息时抛出异常,RabbitMQ根据配置的重试参数自动将消息重新入队,从而实现重试.重试次数和重试间隔等参数可以直接在RabbitMQ的配置中设定,并且RabbitMQ会负责执行这些重试策略。
MAMUAL 模式
配置信息
java
spring:
application:
name: abbitExtensionDemo
rabbitmq:
addresses: xxxx
listener:
simple:
acknowledge-mode: manual # 设置确认模式
retry:
enabled: true # 开启消费者失败重试
initial-interval: 5000ms # 初始失败等待时长为5秒
max-attempts: 5 # 最大重试次数
消费者使用手动确认机制:
java
@Component
@RabbitListener(queues = "RETRY_QUEUE")
public class RetryListener {
@RabbitHandler
public void handle(String messageContent, Channel channel, Message message) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
System.out.println("接收到的消息:" + messageContent);
int n = 10 / 0;
System.out.println("消息消费成功");
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
channel.basicNack(deliveryTag, false, true);
}
}
}
这里也不受重试次数的限制,一直重新入队
这里的 unacked 是指没有收到确认的消息,不是收到否定确认的消息数量,否定确认和肯定确认都是确认,大家不要混淆
当我们将设置不重新入队的时候:
java
channel.basicNack(deliveryTag, false, false);

消息没有进行重试,并且队列也为空了
可以看到,手动确认模式时,重试次数的限制不会像在自动确认模式下那样直接生效,因为是否重试以及何时重试更多地取决于应用程序的逻辑和消费者的实现.
手动确认模式下,消费者需要显式地对消息进行确认,如果消费者在处理消息时遇到异常,可以选择不确认消息使消息可以重新入队,重试的控制权在于应用程序本身,而不是RabbitMQ的内部机制.应用程序可以通过自己的逻辑和利用RabbitMQ的高级特性来实现有效的重试策略
总结
使用重试机制时需要注意:
1.自动确认和默认模式下:程序逻辑异常,多次重试还是失败,消息就会被自动确认,那么消息就丢失
2. 手动确认模式下:程序逻辑异常,多次重试消息依然处理失败,无法被确认,就一直是unacked的状态,导致消息积压