RabbitMQ-Java-死信队列

这里的描述来自官方:

死信交换

来自队列的消息可以是“死信”;也就是说,当发生以下任何事件时,重新发布到交易所:

  • 消费者使用basic.reject或 basic.nack否定确认消息,并将requeue参数设置为false。
  • 消息由于每条消息的 TTL而过期
  • 消息被丢弃,因为它的队列超过了长度限制

请注意,队列到期不会死信其中的消息。

死信交换 (DLX) 是正常的交换。它们可以是任何常用类型,并像往常一样声明。

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

建议使用策略进行配置,因为它允许不涉及应用程序重新部署的 DLX 重新配置。

使用策略配置

要使用策略指定 DLX,请将键“dead-letter-exchange”添加到策略定义中。例如:
在这里插入图片描述
上述策略将 DLX“my-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。

请注意,如果未为队列设置特定的路由键,则其上的消息将使用其所有 原始路由键进行死信。这包括由CC和BCC标头添加的路由密钥(有关这两个标头的详细信息,请参阅Sender-selected 分发)。

有可能形成消息死信的循环。例如,当队列死信消息发送到默认交换时,可能会发生这种情况而没有指定死信路由键。如果在整个循环中没有拒绝,则此类循环中的消息(即两次到达同一队列的消息)将被丢弃。

安全

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

由于 RabbitMQ 3.10 quorum 队列支持至少一次死信, 其中消息在内部打开发布者确认后重新发布。

对消息的死信效应

死信消息会修改其标题:

  • 交换名称被最新的死信交换的名称替换,
  • 路由键可以替换为执行死信队列中指定的键,
  • 如果发生上述情况,CC标头也将被删除,并且
  • BCC标头将根据发件人选择的分发被删除

死信进程将一个数组添加到每个名为x-death的死信消息的标题中。该数组包含每个死信事件的条目,由一对{queue, reason}标识。每个这样的条目都是一个包含几个字段的表:

  • queue:消息在死信之前所在的队列的名称
  • 原因:死字的原因,见下文
  • time:消息被死信的日期和时间,作为 64 位 AMQP 0-9-1 时间戳
  • 交换- 消息发布到的交换(请注意,如果消息多次出现死信,这将是一个死信交换)
  • routing-keys :消息发布时使用的路由密钥(包括CC密钥,但不包括 BCC密钥)
  • count:由于这个原因,这条消息在这个队列中被死信的次数
  • original-expiration(如果消息由于每条消息的 TTL而被死信):消息的原始过期属性。expire属性从死信消息中删除,以防止它在路由到的任何队列中再次过期。

新条目被添加到x-death 数组的开头。如果x-death已经包含具有相同队列和死信原因的条目,则其计数字段将递增,并将移动到数组的开头。

原因是描述为什么消息是死信的名称,并且是以下之一:

  • 拒绝:消息被拒绝,requeue参数设置为false
  • expired :消息 TTL已过期
  • maxlen :超出了允许的最大队列长度
  • delivery_limit:消息返回的次数超过了限制(由仲裁队列的策略参数传递限制设置)。

为第一个死信事件添加了三个顶级标题。他们是

  • x-first-death-reason
  • x-first-death-queue
  • x-first-death-exchange

它们与原始死信事件的原因、队列和交换字段具有相同的值。一旦添加,这些标题将永远不会被修改。

请注意,该数组是最近排序的,因此最近的死信将记录在第一个条目中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要设置一个死信队列,在 RabbitMQ 中,需要进行以下步骤: 1. 首先创建两个队列,分别是正常队列死信队列,以及一个交换机。 2. 正常队列中需要设置死信队列地址。 3. 将交换机和正常队列通过一个 routing key 绑定起来。 4. 当正常队列中的消息过期或者未能被消费时,就会被转发到死信队列中。 以下是一个使用 Java 实现的示例代码: ``` // 创建连接工厂对象 ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); factory.setUsername("guest"); factory.setPassword("guest"); // 创建连接对象 Connection connection = factory.newConnection(); // 创建信道对象 Channel channel = connection.createChannel(); // 定义交换机名称和类型 String exchangeName = "demo.exchange"; String exchangeType = "direct"; // 定义正常队列名称、死信队列名称和 routing key String queueName = "demo.queue"; String deadLetterQueueName = "demo.dead.letter.queue"; String routingKey = "demo.routing.key"; // 定义正常队列属性 Map<String, Object> queueArgs = new HashMap<>(); queueArgs.put("x-message-ttl", 10000); queueArgs.put("x-dead-letter-exchange", exchangeName); queueArgs.put("x-dead-letter-routing-key", deadLetterRoutingKey); // 声明交换机 channel.exchangeDeclare(exchangeName, exchangeType, true, false, null); // 声明正常队列 channel.queueDeclare(queueName, true, false, false, queueArgs); // 声明死信队列 channel.queueDeclare(deadLetterQueueName, true, false, false, null); // 绑定正常队列和 routing key 到交换机 channel.queueBind(queueName, exchangeName, routingKey); // 发送消息到正常队列 channel.basicPublish(exchangeName, routingKey, null, "Hello World!".getBytes()); // 关闭连接 channel.close(); connection.close(); ``` 在以上代码中,我们创建了一个连接工厂对象,使用默认的主机、用户名和密码创建连接对象。然后我们创建了一个信道对象,定义了交换机名称和类型,以及正常队列名称、死信队列名称和 routing key。接着,我们定义了正常队列的属性,其中包括了消息过期时间和死信队列的地址。然后我们声明了交换机、正常队列死信队列,并将正常队列和 routing key 绑定到交换机上。最后,我们发送了一个消息到正常队列,此时如果消息未被消费或者过期了,就会被转发到死信队列中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值