跳至主要内容
版本:4.0

死信交换

什么是死信交换

当以下四种事件中的任何一种发生时,队列中的消息可以被“死信”,这意味着这些消息将在重新发布到一个交换机。

  1. 消息被 负向确认 由使用 rejected 结果的 AMQP 1.0 接收器或使用 basic.rejectbasic.nack 并将 requeue 参数设置为 false 的 AMQP 0.9.1 消费者,或者
  2. 消息由于 每消息 TTL 而过期,或者
  3. 消息因其队列超出 长度限制 而被丢弃,或者
  4. 消息被返回到仲裁队列的次数超过了 传递限制

如果整个 队列过期,则队列中的消息不会被死信。

死信交换 (DLX) 是普通的交换机。它们可以是任何常见的类型,并像普通交换机一样声明。

对于任何给定的队列,DLX 可以由客户端使用 队列的参数 定义,或者在服务器中使用 策略 定义。在策略和参数都指定 DLX 的情况下,参数中指定的 DLX 会覆盖策略中指定的 DLX。

建议使用策略进行配置,因为它允许重新配置 DLX,而无需重新部署应用程序。

使用策略配置死信交换

要使用策略指定 DLX,请将键“dead-letter-exchange”添加到策略定义中。例如

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

上面的策略将 DLX “my-dlx” 应用于所有队列。这只是一个示例,在实践中,不同的队列集通常使用不同的死信设置(或根本不使用)。

类似地,可以通过将键“dead-letter-routing-key”添加到策略中来指定显式路由键。

策略也可以使用管理插件定义,有关详细信息,请参见 策略文档

使用可选队列参数配置死信交换

要为队列设置 DLX,请在声明队列时指定可选的 x-dead-letter-exchange 参数。该值必须是同一个虚拟主机中的交换机名称

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);

上面的代码声明了一个名为 some.exchange.name 的新交换机,并将这个新交换机设置为新创建的队列的死信交换机。请注意,交换机不必在声明队列时声明,但应在需要死信消息之前存在。如果它丢失,则消息将被静默丢弃。

您还可以指定在死信消息时使用的路由键。如果没有设置路由键,将使用消息自身的路由键。

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

当指定死信交换机时,除了对声明的队列进行通常的配置权限外,用户还必须对该队列具有读权限,对死信交换机具有写权限。权限在声明队列时进行验证。

路由死信消息

死信消息将被路由到它们的死信交换机,方法如下

  • 使用为它们所在的队列指定的路由键;或者,如果没有设置
  • 使用它们最初发布时的相同路由键

例如,如果您将一条消息发布到具有 foo 路由键的交换机,并且该消息被死信,它将使用 foo 路由键发布到其死信交换机。如果消息最初所在的队列使用 x-dead-letter-routing-key 设置为 bar 声明,则消息将使用 bar 路由键发布到其死信交换机。

请注意,如果为队列没有设置特定的路由键,则队列上的消息将使用所有原始路由键进行死信。这包括由 CCBCC 标头添加的路由键(有关这两个标头的详细信息,请参阅 发送方选择的分配)。

死信循环

有可能形成一个消息死信循环,其中相同的消息两次到达相同的队列。例如,当队列“死信”消息到默认交换机而没有指定死信路由键时,就会发生这种情况。为了防止 RabbitMQ 中自动无限消息循环,RabbitMQ 会检测到循环,并在整个循环中没有拒绝的情况下丢弃消息。

安全

默认情况下,死信消息将在没有内部打开生产者 确认 的情况下重新发布。因此,在集群式 RabbitMQ 环境中使用 DLX 并不保证安全。消息在发布到 DLX 目标队列后立即从原始队列中删除。这确保了不会出现可能耗尽代理资源的过量消息积压。但是,如果目标队列不可用以接收消息,则消息可能会丢失。

从 RabbitMQ 3.10 开始,仲裁队列支持 至少一次死信,其中消息在内部打开生产者确认的情况下重新发布。

死信对消息的影响

死信消息会修改其标头

  • 交换机名称将被最新的死信交换机名称替换
  • 路由键可能会被执行死信的队列中指定的路由键替换(即配置的 dead-letter-routing-key),
  • 如果发生上述情况,CC 标头也将被删除,并且
  • BCC 标头将根据 发送方选择的分配 被删除

一条消息可以被多次死信。每次死信消息时,都会在消息标头中记录此事件。为了防止标头无限制地增长,死信事件历史记录将被 {Queue, Reason} 对压缩。

AMQP 1.0 消息将包含一个 消息注释,其符号键为 x-opt-deaths,值为一个 数组 映射。AMQP 0.9.1 消息将包含一个 x-death 标头,其值为一个数组。AMQP 1.0 和 AMQP 0.9.1 中的数组都按最近程度排序,即最新的死信事件记录在第一个数组元素中。

下表描述了 AMQP 1.0 映射键值对和 AMQP 0.9.1 数组元素表。所有 AMQP 1.0 键都是 symbol 类型的。AMQP 1.0 客户端不应依赖映射的键值对的顺序。

AMQP 1.0 键AMQP 1.0 值类型AMQP 0.9.1 键AMQP 0.9.1 值类型描述
queuestringqueuelongstr从该队列中死信的队列名称。
reasonsymbolreasonlongstr死信消息的原因(下面描述)。
countulongcountlong从该队列中因该原因死信的消息次数。
first-timetimestamp第一次从该队列中因该原因死信消息的时间。
last-timetimestamp最后一次从该队列中因该原因死信消息的时间。
timetimestamp第一次从该队列中因该原因死信消息的时间。
exchangestringexchangelongstr第一次从该队列中因该原因死信消息之前,此消息发布到的交换机。
routing-keysstring 数组routing-keyslongstr 数组第一次从该队列中因该原因死信消息之前,此消息的路由键(包括 CC,但不包括 BCC)。
ttluint第一次从该队列中因该原因死信消息之前,AMQP 1.0 标头ttl(以毫秒为单位的生存时间)。
original-expirationlongstr第一次从该队列中因该原因死信消息之前,此消息的原始 expiration 属性。

AMQP 1.0 ttl 和 AMQP 0.9.1 original-expiration 是可选的,并且被记录下来,因为原始消息的 TTL 在死信时会从消息中删除,以防止它在路由到的任何队列中再次过期。

reason 是一个描述死信原因的名称,可以是以下之一

此外,以下六个 AMQP 1.0 消息注释或 AMQP 0.9.1 标头将为第一次死信事件添加

  1. x-first-death-queue:此消息第一次死信的队列。
  2. x-first-death-reason:此消息第一次死信的原因。
  3. x-first-death-exchange:此消息第一次死信之前发布到的交换机。
  4. x-last-death-queue:此消息最后一次死信的队列。
  5. x-last-death-reason:此消息最后一次死信的原因。
  6. x-last-death-exchange:此消息最后一次死信之前发布到的交换机。

x-first-* 注释永远不会被修改。 只要消息被后续死信处理,x-last-* 注释就会更新。

© 2024 RabbitMQ. All rights reserved.