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

说明

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

答案

RabbitMQ官方文档

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



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

    目录 一.背景 二.Future接口 1.入门案例 2.Future接口 三.CompletableFuture类 1.基础说明 2.核心方法 2.1 实例方法 2.2 计算方法 2.3 结果获取方法 ...

  2. TENGSHE-OS-渗透测试系统-win11版

    下载ISO文件 创建新的虚拟机 VM17 已支持直接创建 win11 x64 稍后安装系统 选中win11 修改路径 win11需要设置8位加密密码 勾选安全引导 根据自身情况选择 默认即可 150G ...

  3. 音频处理库性能对比:计算mel频谱的速度哪个更快?

    介绍 音频信号处理在各种应用中都发挥着重要的作用,如语音识别.音乐信息检索.语音合成等.其中,Mel频谱是一种常用的频域特征表示方法,用于描述人类听觉系统对频率的敏感程度. 在深度学习音频领域,mel ...

  4. Istio数据面新模式:Ambient Mesh技术解析

    摘要:Ambient Mesh以一种更符合大规模落地要求的形态出现,克服了大多数Sidecar模式的固有缺陷,让用户无需再感知网格相关组件,真正将网格下沉为基础设施. 本文分享自华为云社区<华为 ...

  5. 【问题排查篇】一次业务问题对 ES 的 cardinality 原理探究

    作者:京东科技 王长春 业务问题 小编工作中负责业务的一个服务端系统,使用了 Elasticsearch 服务做数据存储,业务运营人员反馈,用户在使用该产品时发现,用户后台统计的订单笔数和导出的订单笔 ...

  6. 芯烨小票打印机PHP打印代码

    芯烨小票打印机PHP打印代码,案例中打印机为58mm打印机 58mm的机器,一行打印16个汉字,32个字母 80mm的机器,一行打印24个汉字,48个字母 本次添加了补打空格的代码,根据商品名称计算后 ...

  7. 2022-08-03:以下go语言代码输出什么?A:2;B:3;C:1;D:0。 package main import “fmt“ func main() { slice := []i

    2022-08-03:以下go语言代码输出什么?A:2:B:3:C:1:D:0. package main import "fmt" func main() { slice := ...

  8. 2022-07-26:以下go语言代码输出什么?A:5;B:hello;C:编译错误;D:运行错误。 package main import ( “fmt“ ) type integer in

    2022-07-26:以下go语言代码输出什么?A:5:B:hello:C:编译错误:D:运行错误. package main import ( "fmt" ) type inte ...

  9. 2022-07-04:以下go语言代码输出什么?A:true;B:false;C:编译错误。 package main import “fmt“ func main() { fmt.Pri

    2022-07-04:以下go语言代码输出什么?A:true:B:false:C:编译错误. package main import "fmt" func main() { fmt ...

  10. 2022-05-26:void add(int L, int R, int C)代表在arr[L...R]上每个数加C, int get(int L, int R)代表查询arr[L...R]上的累加

    2022-05-26:void add(int L, int R, int C)代表在arr[L-R]上每个数加C, int get(int L, int R)代表查询arr[L-R]上的累加和, 假 ...