【RabbitMQ】当队列中消息数量超过最大长度的淘汰策略

说明

最近在研究RabbitMQ如何实现延时队列时发现消息进入死信队列的情况之一就是当消息数量超过队列设置的最大长度时会被丢入死信队列,看到这时我就产生了一个疑问,到底是最后插入的消息还是最早插入的消息会被丢入死信队列呢?遗憾的是看了几篇博客都是模棱两可的答案,还有的说超过数量后该消息会被放入死信队列,看完之后还是对这个问题将信将疑。所以我决定去探究一下正确答案

答案

RabbitMQ官方文档

遇事不决肯定是先看官方文档最靠谱啦,在官网中扒拉了半天终于找到说明这个问题的页面了,就是上面引用的链接,重点如下:



翻译过来就是:在RabbitMQ中,当消息的数量或大小达到限制后,默认的操作是删除最旧的消息或将最旧的消息放入死信队列,这取决于该队列是否配置了死信队列。 我们可以通过使用overflow配置指定的处理策略,如果overflow被设置为reject-publishreject-publish-dlx,那么会将最新插入的消息丢弃。如果该队列开启了confirm机制,发布者会收到nack的信息,如果一个消息被路由到多个队列,只要其中一个队列拒绝发布者就会收到nack消息,但是没被拒绝的队列可以正确接收到消息。reject-publishreject-publish-dlx的区别是后者还会将拒绝的消息放入死信队列。

验证

下面我们使用demo来验证一下各个策略的现象

默认策略(drop-head)

  1. application.yml配置如下:
  rabbitmq:
host: 127.0.0.1
port: 5672
username: username
password: password
virtual-host: /test
publisher-confirm-type: correlated # 配置启用confirm机制
  1. 使用RabbitMQConfig创建业务队列和对应死信队列
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap; @Configuration
public class RabbitMQConfig { public static final String DELAY_EXCHANGE_NAME = "delay.business.exchange";
public static final String DELAY_QUEUE_NAME = "delay.business.queue";
public static final String DELAY_QUEUE_ROUTING_KEY = "delay.business.queue.routingKey";
public static final String DEAD_LETTER_EXCHANGE_NAME = "dead.letter.exchange";
public static final String DEAD_LETTER_QUEUE_NAME = "dead.letter.queue";
public static final String DEAD_LETTER_QUEUE_ROUTING_KEY = "dead.letter.queue.routingKey"; // 声明延迟队列交换机
@Bean("delayExchange")
public DirectExchange delayExchange(){
return new DirectExchange(DELAY_EXCHANGE_NAME);
} // 声明死信队列交换机
@Bean("deadLetterExchange")
public DirectExchange deadLetterExchange(){
return new DirectExchange(DEAD_LETTER_EXCHANGE_NAME);
} // 声明延时队列
@Bean("delayQueue")
public Queue delayQueue(){
HashMap<String, Object> map = new HashMap<>();
// x-dead-letter-exchange 这里声明当前队列绑定的死信交换机
map.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE_NAME);
// x-dead-letter-routing-key 这里声明当前队列的死信路由key
map.put("x-dead-letter-routing-key", DEAD_LETTER_QUEUE_ROUTING_KEY);
// 设置该队列最大消息数
map.put("x-max-length", 10);
map.put("x-overflow", "reject-publish");
return QueueBuilder.durable(DELAY_QUEUE_NAME).withArguments(map).build();
} // 声明死信队列
@Bean("deadLetterQueue")
public Queue deadLetterQueue(){
return new Queue(DEAD_LETTER_QUEUE_NAME);
} // 声明延时队列的绑定关系
@Bean
public Binding delayBinding(@Qualifier("delayExchange") DirectExchange directExchange,
@Qualifier("delayQueue") Queue queue){
return BindingBuilder.bind(queue).to(directExchange).with(DELAY_QUEUE_ROUTING_KEY);
} // 声明死信队列的绑定关系
@Bean
public Binding deadLetterBinding(@Qualifier("deadLetterExchange") DirectExchange directExchange,
@Qualifier("deadLetterQueue") Queue queue){
return BindingBuilder.bind(queue).to(directExchange).with(DEAD_LETTER_QUEUE_ROUTING_KEY);
}
}

注意,这里我们并没有设置overflow参数,所以采用的是默认配置

3. 创建消费者监听死信队列

import com.rabbitmq.client.Channel;
import com.whs.edws.config.RabbitMQConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException; @Slf4j
@Component
public class MaxLengthConsumer { @RabbitListener(queues = RabbitMQConfig.DEAD_LETTER_QUEUE_NAME)
public void receive(Message message, Channel channel) throws IOException {
String s = new String(message.getBody());
log.info("死信队列消费者接收到消息:" + s);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
  1. 创建测试方法发送消息
@Test
void maxLengthTestPublish(){
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* @param correlationData 相关配置信息
* @param ack 消息队列是否成功收到消息
* @param cause 错误原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
logger.info("消息发送成功:" + correlationData.getId());
} else {
logger.error("消息发送失败:" + correlationData.getId());
logger.error("错误原因:" + cause);
}
}
});
for (int i = 0; i < 11; i++) {
CorrelationData correlationData = new CorrelationData();
correlationData.setId(String.valueOf(i));
rabbitTemplate.convertAndSend(RabbitMQConfig.DELAY_EXCHANGE_NAME, RabbitMQConfig.DELAY_QUEUE_ROUTING_KEY, String.valueOf(i), correlationData);
}
}
  1. 运行结果

    看上面的代码可知,我们设置了队列大小为10,但是我们向队列发送了11条消息,最后日志打印如下:

    2023-07-18 02:37:52.941 INFO 24308 --- [ntContainer#1-1] com.edws.rabbitmq.MaxLengthConsumer : 死信队列消费者接收到消息:0

    和官方文档说的一样,默认最旧的一条消息被放入死信队列

reject-publish策略

reject-publish策略的验证代码只需在默认策略的基础上加上配置即可,我们在定义队列的时候加上配置

// 指定超过队列长度后的策略
map.put("x-overflow", "reject-publish");

执行方法,打印的结果为:

2023-07-18 02:45:07.242  INFO 22328 --- [nectionFactory2] o.s.amqp.rabbit.core.RabbitTemplate      : 消息发送失败:10
2023-07-18 02:45:07.242 INFO 22328 --- [nectionFactory2] o.s.amqp.rabbit.core.RabbitTemplate : 错误原因:null

通过日志可以看到,最新插入的消息被丢弃了。至于cause为什么是null,我没找到原因,如果了解的朋友可以在评论里讨论一下

reject-publish-dlx策略

reject-publish-dlx策略的代码也是只需要在默认代码中加一行配置即可

// 指定超过队列长度后的策略
map.put("x-overflow", "reject-publish-dlx");

打印结果如下:

2023-07-18 02:49:13.246  INFO 10488 --- [nectionFactory2] o.s.amqp.rabbit.core.RabbitTemplate      : 消息发送失败:10
2023-07-18 02:49:13.246 INFO 10488 --- [nectionFactory2] o.s.amqp.rabbit.core.RabbitTemplate : 错误原因:null
2023-07-18 02:49:13.252 INFO 10488 --- [ntContainer#1-1] com.whs.edws.rabbitmq.MaxLengthConsumer : 死信队列消费者接收到消息:10

通过日志可以看出,最新的一条消息被拒绝且被放入死信队列

【RabbitMQ】当队列中消息数量超过最大长度的淘汰策略的更多相关文章

  1. rabbitmq队列中消息过期配置

    最近公司某个行情推送的rabbitmq服务器由于客户端异常导致rabbitmq队列中消息快速堆积,还曾导致过内存积压导致rabbitmq客户端被block的情况.考虑到行情信息从业务上来说可以丢失部分 ...

  2. RabbitMQ镜像模式双节点部署时故障转移过程中队列中消息的状态

    场景 现有节点Node1和Node2,建立Exchange:yu.exchange,创建队列yu1.queue镜像队列master位于Node1,yu2.queue镜像队列位于Node2,使用topi ...

  3. RabbitMQ获取队列的消息数目

    使用RabbitMQ,业务需求,想要知道队列中还有多少待消费待数据. 方式一: @Value("${spring.rabbitmq.host}") private String h ...

  4. 《剑指offer》数组中出现次数超过数组长度一半的数字

    题目: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2.如 ...

  5. 【剑指offer】数组中出现次数超过数组长度一半的数字,C++实现

    原创博文,转载请注明出处! # 题目 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过 ...

  6. 找出整数数组中出现次数超过数组长度一半的元素(Java)

    Question:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字 package com.study.zhipengs.test; import java.util.Arrays; im ...

  7. activemq读取剩余消息队列中消息的数量

    先上原文链接: http://blog.csdn.net/bodybo/article/details/5647968  ActiveMQ在C#中的应用 ActiveMQ是个好东东,不必多说.Acti ...

  8. RabbitMQ 延迟队列,消息延迟推送

    目录 应用场景 消息延迟推送的实现 测试结果 应用场景 目前常见的应用软件都有消息的延迟推送的影子,应用也极为广泛,例如: 淘宝七天自动确认收货.在我们签收商品后,物流系统会在七天后延时发送一个消息给 ...

  9. 显示推送数据到mq成功,但是mq管理器中消息数量没增长

    看服务器上的mq配置,看看mq_log,是不是存储满了?

  10. 【RabbitMQ】一文带你搞定RabbitMQ死信队列

    本文口味:爆炒鱿鱼   预计阅读:15分钟 一.说明 RabbitMQ是流行的开源消息队列系统,使用erlang语言开发,由于其社区活跃度高,维护更新较快,性能稳定,深得很多企业的欢心(当然,也包括我 ...

随机推荐

  1. 帝国cms 批量删除或者清空classurl(二级域名绑定)

      update phome_enewsclass set classurl= null ;

  2. ASP.NET Core - 缓存之分布式缓存

    分布式缓存是由多个应用服务器共享的缓存,通常作为访问它的应用服务器的外部服务进行维护. 分布式缓存可以提高 ASP.NET Core 应用的性能和可伸缩性,尤其是当应用由云服务或服务器场托管时. 与其 ...

  3. Swift Codable协议实战:快速、简单、高效地完成JSON和Model转换!

    前言 Codable 是 Swift 4.0 引入的一种协议,它是一个组合协议,由 Decodable 和 Encodable 两个协议组成.它的作用是将模型对象转换为 JSON 或者是其它的数据格式 ...

  4. 快速上手Linux核心命令(十):Linux安装软件

    目录 前言 rpm rpm包管理器 yum 自动化RPM包管理工具 前言 这期呢主要说一说Linux中包软件管理相关命令,这一期的命令虽然只有两个.但 软件包的安装和卸载都是我们平常最常用的,需要熟练 ...

  5. 继承 extends

    首先是基础的继承关系,用extend就可以继承. 再者是继承的东西,包括:变量(也包括类变量).全部非私有的属性和方法(除了父类的构造方法) 注:构造方法 class C{ public C() { ...

  6. vue3 + vite 多项目多模块打包

    vue3 + vite 多项目多模块打包 本示例基于vite-plugin-html插件,实现多个独立项目共存,共享组件和依赖,运行.打包互不干扰. npm create vite@latest 兼容 ...

  7. 2023-05-10:给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表 如果在二叉树中,存在一条一直向下的路径 且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,

    2023-05-10:给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表 如果在二叉树中,存在一条一直向下的路径 且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值, ...

  8. #Powerquery pq从入门到精通,笔记梳理

    详细视频可以B站搜索,ms_excel博主,看leila的视频翻译课,powerquery从入门到精通. 后续也会根据自己工作来更新这部分的实际用法.

  9. 音视频八股文(7)-- 音频aac adts

    AAC介绍 AAC(Advanced Audio Coding)是一种现代的音频编码技术,用于数字音频的传输和存储领域.AAC是MPEG-2和MPEG-4标准中的一部分,可提供更高质量的音频数据,并且 ...

  10. 2020-08-21:网络IO模型有哪些?

    福哥答案2020-08-21: 福哥口诀法:阻非复信异(阻塞.非阻塞.多路复用.信号驱动.异步) [知乎答案](https://www.zhihu.com/question/416128059)操作系 ...