【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. Lexicographic Order

    Lexicographic Order (https://codeforces.com/group/L9GOcnr1dm/contest/422381/problem/L) 比较简单的一道题目,主要理 ...

  2. 回车,换行,转义字符“\r”,“\n”是什么关系?

    1."回车"这个名词的来历. 关于"回车键"的来历,还得从机械英文打字机说起.在机械英文打字机上,有一个部件叫"字车",每打一个字符(原为单 ...

  3. 快速上手Linux核心命令(三):文件和目录操作命令

    @ 目录 前言 cd 切换目录 pwd 显示当前路径 ls 显示目录下内容及相关属性信息 mkdir 创建目录 tree 以树形结构显示目录下的内容 touch 创建空白文件或改变文件的时间戳属性 c ...

  4. 【Python基础】字符串的基本使用

    b6f9d807-edb2-4e0a-b554-fae322343bee 字符串是Python中最基本的数据类型之一.它是由一系列字符组成的不可变序列.这意味着一旦创建了一个字符串,就不能直接修改它的 ...

  5. Feign踩坑源码分析--@FeignClient注入容器

    一. @EnableFeignClients 1.1.类介绍 从上面注释可以看出是扫描声明了@FeignClient接口的类,还引入了 FeignClientsRegistrar类,从字面意思可以看出 ...

  6. 2023-04-15:ffmpeg的filter_audio.c的功能是生成一个正弦波音频,然后通过简单的滤镜链,最后输出数据的MD5校验和。请用go语言改写。

    2023-04-15:ffmpeg的filter_audio.c的功能是生成一个正弦波音频,然后通过简单的滤镜链,最后输出数据的MD5校验和.请用go语言改写. 答案2023-04-15: 代码见gi ...

  7. 2023-01-02:某天,小美在玩一款游戏,游戏开始时,有n台机器, 每台机器都有一个能量水平,分别为a1、a2、…、an, 小美每次操作可以选其中的一台机器,假设选的是第i台, 那小美可以将其变成

    2023-01-02:某天,小美在玩一款游戏,游戏开始时,有n台机器, 每台机器都有一个能量水平,分别为a1.a2.-.an, 小美每次操作可以选其中的一台机器,假设选的是第i台, 那小美可以将其变成 ...

  8. 2020-11-16:手写代码:leetcode第406题。假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。

    福哥答案2020-11-16: ①排序.按照[身高]降序排列.如果[身高]一样,按照[人数]升序排列.②插入.遍历这个队列,按照[人数]插入相应位置. 采用leetcode里的代码,golang代码如 ...

  9. 【模型部署 01】C++实现分类模型(以GoogLeNet为例)在OpenCV DNN、ONNXRuntime、TensorRT、OpenVINO上的推理部署

    深度学习领域常用的基于CPU/GPU的推理方式有OpenCV DNN.ONNXRuntime.TensorRT以及OpenVINO.这几种方式的推理过程可以统一用下图来概述.整体可分为模型初始化部分和 ...

  10. 一分钟学一个 Linux 命令 - ls

    前言 大家好,我是 god23bin.今天我给大家带来的是 Linux 命令系列,每天只需一分钟,记住一个 Linux 命令不成问题.今天,我们要介绍的是一个常用而又强大的命令:ls(list). 什 ...