RabbitMQ之死信队列、延迟队列和懒队列

目录

死信队列

何时会产生死信

死信队列的配置方式

参数x-dead-letter-routing-key

如何确定一个消息是不是死信

延迟队列

懒队列

声明懒队列的两种方式

参数声明

策略指定


死信队列

死信队列是RabbitMQ中非常重要的一个特性。简单理解,他是RabbitMQ对于未能正常消费的消息进行的一种补救机制。死信队列也是一个普通的队列,同样可以在队列上声明消费者,继续对消息进行消费处理。

​ 对于死信队列,在RabbitMQ中主要涉及到几个参数:

x-dead-letter-exchange指定一个交换机作为死信交换机,然后x-dead-letter-routing-key指定交换机的RoutingKey。而接下来,死信交换机就可以像普通交换机一样,通过RoutingKey将消息转发到对应的死信队列中。

何时会产生死信

以下三种情况,RabbitMQ会将一个正常消息转成死信

  1. 消息被消费者确认拒绝。消费者把requeue参数设置为true(false),并且在消费后,向RabbitMQ返回拒绝。channel.basicReject或者channel.basicNack。

  2. 消息达到预设的TTL时限还一直没有被消费。TTL即最长存活时间 Time-To-Live 。消息在队列中保存时间超过这个TTL,即会被认为死亡。如果配置了死信队列,死亡的消息会进入该队列。

设置TTL有两种方式,一是通过配置策略指定,另一种是给队列单独声明TTL。

策略配置方式: Web管理平台配置 或者 使用指令配置 30000为毫秒单位

rabbitmqctl set_policy TTL ".*" '{"message-ttl":30000}' --apply-to queues

声明队列时指定:同样可以在Web管理平台配置,也可以在代码中配置

java 复制代码
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 60000);
channel.queueDeclare("myqueue", false, false, false, args);
  1. 消息由于队列已经达到最长长度限制而被丢掉。

死信队列的配置方式

RabbitMQ中有两种方式可以声明死信队列,一种是针对某个单独队列指定对应的死信队列。另一种就是以策略的方式进行批量死信队列的配置。

针对多个队列,可以使用策略方式,配置统一的死信队列:

java 复制代码
rabbitmqctl set_policy DLX ".*" '{"dead-letter-exchange":"my-dlx"}' --apply-to queues

针对队列单独指定死信队列的方式主要是之前提到的三个属性:

java 复制代码
channel.exchangeDeclare("deadexchange", "direct");
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "deadexchange");
channel.queueDeclare("myqueue", false, false, false, args);

这些参数,也可以在RabbitMQ的管理页面进行配置。例如配置策略时:

在对队列进行配置时,只有Classic经典队列和Quorum仲裁队列才能配置死信队列,而目前Stream流式队列,并不支持配置死信队列。


参数x-dead-letter-routing-key

当消息被投递到死信队列时,它会携带原始消息的routing key,除非配置了 x-dead-letter-routing-key 参数,此时会将routing key替换为指定的值。需要注意的是,这个过程是由 RabbitMQ 管理的,而不经过消息发送者的确认,因此在死信消息转移的过程中,无法由发送者确保消息的安全性。

如何确定一个消息是不是死信

消息被作为死信转移到死信队列后,会在Header当中增加一些消息。会加上第一次成为死信的三个属性,并且这三个属性在以后的传递过程中都不会更改。

javascript 复制代码
x-first-death-reason
x-first-death-queue
x-first-death-exchange

延迟队列

RabbitMQ中,是不存在延迟队列的功能的,而通常如果要用到延迟队列,就会采用TTL+死信队列的方式来处理。RabbitMQ提供了一个rabbitmq_delayed_message_exchange插件,可以实现延迟队列的功能,但是并没有集成到官方的发布包当中,需要单独去下载。

TTL+死信队列实现逻辑,创建一个普通交换机跟队列,给队列设置TTL超时时间,但是不给这个队列绑定消费者,给这个普通队列绑定死信交换机,等到消息超时就会发送到死信交换机,给死信交换机配一个消费者来消费。这种方式可以实现延迟队列的功能。但在一些情况下可能会有一些局限性,比如无法实现非常精确的延迟。

为了更方便、更灵活地实现延迟队列功能,可以使用RabbitMQ提供的插件rabbitmq_delayed_message_exchange,这个插件为RabbitMQ提供了原生的延迟队列支持,允许在不使用死信队列的情况下直接实现延迟消息的发送和消费。


懒队列

RabbitMQ从3.6.0版本开始,就引入了懒队列(Lazy Queue)的概念。懒队列会尽可能早的将消息内容保存到硬盘当中,并且只有在用户请求到时,才临时从硬盘加载到RAM内存当中。懒队列的设计目标是为了支持非常长的队列(数百万级别)。队列可能会因为一些原因变得非常长也就是数据堆积。比如消费者服务宕机了、有一个突然的消息高峰,生产者生产消息超过消费者、消费者消费太慢了,都有可能造成数据堆积。

默认情况下,RabbitMQ接收到消息时,会保存到内存以便使用,同时把消息写到硬盘。但是,消息写入硬盘的过程中,是会阻塞队列的。RabbitMQ虽然针对写入硬盘速度做了很多算法优化,但是在长队列中,依然表现不是很理想,所以就有了懒队列的出现。​ 懒队列会尝试尽可能早的把消息写到硬盘中。这意味着在正常操作的大多数情况下,RAM(内存)中要保存的消息要少得多(数据都在硬盘中)。

声明懒队列的两种方式

参数声明

在代码中可以通过x-queue-mode参数指定

java 复制代码
  Map<String, Object> args = new HashMap<String, Object>();
  args.put("x-queue-mode", "lazy");
  channel.queueDeclare("myqueue", false, false, false, args);
策略指定
java 复制代码
rabbitmqctl set_policy Lazy "^lazy-queue$" '{"queue-mode":"default"}' --apply-to queues

当一个队列被声明为懒队列,那即使队列被设定为不持久化,消息依然会写入到硬盘中。如果是在集群模式中使用,会给集群资源带来很大的负担。

懒队列适合消息量大且长期有堆积的队列,可以减少内存使用,加快消费速度。但是这是以大量消耗集群的网络及磁盘IO为代价的

相关推荐
哎呦没13 分钟前
SpringBoot框架下的资产管理自动化
java·spring boot·后端
2401_8576009515 分钟前
SpringBoot框架的企业资产管理自动化
spring boot·后端·自动化
m0_571957582 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
魔道不误砍柴功4 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2344 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨4 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
Chrikk6 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*6 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue6 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man6 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang