RabbitMQ(四)RabbitMQ死信邮箱(DLX)

本文介绍了RabbitMQ中DLX(Dead-Letter-Exchange)的概念及其用途,详细阐述了如何通过DLX处理死信,并提供了Java SDK设置示例及TTL与DLX结合使用的应用场景。

DLX,Dead-Letter-Exchange(死信邮箱)

利用DLX,当消息在一个队列中变成死信后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。消息变成死信一向有以下几种情况:

  • 消息被拒绝(basic.reject or basic.nack)并且requeue=false
  • 消息TTL过期
  • 队列达到最大长度

DLX也是一下正常的Exchange同一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性,当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange中去,进而被路由到另一个队列,publish可以监听这个队列中消息做相应的处理,这个特性可以弥补RabbitMQ 3.0.0以前支持的immediate参数中的向publish确认的功能。


JAVA的SDK设置

channel.exchangeDeclare("some.exchange.name", "direct");

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "some.exchange.name");
channel.queueDeclare("myqueue", false, false, false, args);

你也可以为这个DLX指定routing key,如果没有特殊指定,则使用原队列的routing key 

args.put("x-dead-letter-routing-key", "some-routing-key");

还可以使用policy来配置:

rabbitmqctl set_policy DLX ".*" '{"dead-letter-exchange":"my-dlx"}' --apply-to queues

“死信”被republish时,会在消息的header中增加一个叫做“x-death"的数组内容,包含了以下字段内容:

  • queue - the name of the queue the message was in before it was dead-lettered,
  • reason - see below,
  • time - the date and time the message was dead lettered as a 64-bit AMQP format timestamp,
  • exchange - the exchange the message was published to (note that this will be a dead letter exchange if the message is dead lettered multiple times), and
  • routing-keys - the routing keys (including CC keys but excluding BCC ones) the message was published with.
  • original-expiration (if the message was dead-letterered due to per-message TTL) - the originalexpiration property of the message. The expiration property is removed from the message on dead-lettering in order to prevent it from expiring again in any queues it is routed to.

The reason is a name describing why the message was dead-lettered and is one of the following:

  • rejected - the message was rejected with requeue=false,
  • expired - the TTL of the message expired; or
  • maxlen - the maximum allowed queue length was exceeded.

在使用immediate参数时,如果消费者拒收消息这个应答是没有办法通知到publish的,但是利用DLX,拒收的消息会变成“死信”,这个死信会被republish到DLX路由的队列中去,分析这个新的信息的header中的x-death内容,可以判断出消息变成死信的原因,这样publish端可以做更灵活的处理。


TTL和DLX结合使用的一种场景


假如我需要一个这样的应用场景,consumer可以无条件退出,退出后我希望它监听的队列也一并删除掉,但是如果队列如果有消息,要求消息能被转发到DLX队列。


如果用queue的AUTO_DELETE功能,则consumer退出后,不管队列中是否有消息,队列都会被删除,这个方法不行。

我们可以这样的变通实现:


1、将队列的"x-expires"参数为300S,即队列在没有消费者空闲300S后将被自动删除 )


2、将队列的"x-message-ttl"参数为30S,即消息只存活30S


3、在生产者publish消息时,设置 immediate属性,即要求一publish时一定有一个消费者存在


这样子生产者不停的将消息成功放进了队列,由于publish时判断了immediate属性,也就是说publish消息时,消费者是一定存在的,假如由于消费者处理的不及时导致消息有所堆积,在30S内还没有处理的话,这些消息将会被republish到DLX路由的队列中保存,即使在这时消费者异常退出了,也没关系,因为队列至少也将在300S后才自动删除,有足够长的时间让所有消费都republish至DLX队列


这个应用场景是我自己乱想的,纯粹是为了学习RabbitMQ而想的,实际应用中可以有其它代替方案来实现,就当学习了。


RabbitMQ 中,消息进入死信队列(Dead Letter Queue, DLQ)的机制是一种用于处理无法被正常消费的消息的方式。该机制通过配置特定的参数,将满足某些条件的消息自动转发到指定的死信交换机(Dead Letter Exchange, DLX),再由该交换机将消息路由至死信队列中。 ### 消息进入死信队列的条件 1. **消息被拒绝(basic.reject 或 basic.nack)且设置不重新入队** 当消费者调用 `basic.reject` 或 `basic.nack` 方法,并将 `requeue` 参数设为 `false` 时,消息不会重新放回原队列,而是被发送到死信交换机[^2]。 2. **消息过期(TTL 超时)** 如果队列或消息本身设置了生存时间(Time-To-Live, TTL),当消息在队列中等待的时间超过该设定值,消息会被标记为死信并发送至死信交换机[^1]。 3. **队列达到最大长度限制** 若队列设置了最大长度(`x-max-length`),当队列已满,最早进入队列的消息会被移除并发送到死信交换机[^1]。 4. **消息在队列中等待期间其 TTL 已过期** 即使消息尚未被投递给消费者,如果在其等待过程中 TTL 过期,也会触发死信流程[^5]。 ### 消息进入死信队列的流程 1. **声明死信交换机和死信队列** 首先需要声明一个死信交换机(DLX)以及一个与之绑定的死信队列(DLQ)。通常使用 `fanout` 类型的交换机作为死信交换机,因为它会将消息广播给所有绑定的队列[^3]。 2. **配置原队列的死信参数** 在声明原队列时,需设置以下两个参数: - `x-dead-letter-exchange`:指定死信交换机的名称。 - `x-message-ttl`(可选):设置消息的 TTL 值。 - `x-max-length`(可选):设置队列的最大长度限制。 示例代码如下: ```java Map<String, Object> args = new HashMap<>(); args.put("x-dead-letter-exchange", "my.dlx"); args.put("x-message-ttl", 10000); // 消息存活时间为10秒 channel.queueDeclare("my-queue", false, false, false, args); ``` 3. **绑定死信交换机与死信队列** 将死信交换机与死信队列进行绑定,确保死信消息可以被正确路由到目标队列。 4. **消费死信队列中的消息** 死信队列中的消息可以由专门的消费者进行监听和处理。例如,可以记录日志、重试发送、更新数据库状态等操作[^4]。 示例代码如下: ```java @RabbitListener(queues = RabbitDeadLetterConfig.REDIRECT_QUEUE) @Component public class RedirectQueueConsumer { @RabbitHandler public void fromDeadLetter(Integer number){ System.out.println("RedirectQueueConsumer : " + number); // 对应的业务操作,如更新订单状态、释放库存等 } } ``` ### 总结 通过合理配置死信机制,可以在消息处理失败或超时的情况下,将这些“异常”消息集中管理,避免丢失或无限循环重试,从而提高系统的容错能力和可观测性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值