目录
死信队列
何时会产生死信
死信队列的配置方式
参数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管理平台配置,也可以在代码中配置
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 60000);
channel.queueDeclare("myqueue", false, false, false, args);
3. 消息由于队列已经达到最长长度限制而被丢掉。
死信队列的配置方式
RabbitMQ中有两种方式可以声明死信队列,一种是针对某个单独队列指定对应的死信队列。另一种就是以策略的方式进行批量死信队列的配置。
针对多个队列,可以使用策略方式,配置统一的死信队列:
rabbitmqctl set_policy DLX ".*" '{"dead-letter-exchange":"my-dlx"}' --apply-to queues
针对队列单独指定死信队列的方式主要是之前提到的三个属性:
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当中增加一些消息。会加上第一次成为死信的三个属性,并且这三个属性在以后的传递过程中都不会更改。
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参数指定
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-queue-mode", "lazy");
channel.queueDeclare("myqueue", false, false, false, args);
策略指定
rabbitmqctl set_policy Lazy "^lazy-queue$" '{"queue-mode":"default"}' --apply-to queues
当一个队列被声明为懒队列,那即使队列被设定为不持久化,消息依然会写入到硬盘中。如果是在集群模式中使用,会给集群资源带来很大的负担。
懒队列适合消息量大且长期有堆积的队列,可以减少内存使用,加快消费速度。但是这是以大量消耗集群的网络及磁盘IO为代价的。