服务总线死信队列概述

Azure 服务总线队列和主题订阅提供一个名为“死信队列”(DLQ) 的辅助子队列。 死信队列不需要显式地创建,也不能独立于主实体进行删除或管理。

本文介绍服务总线中的死信队列。 GitHub 上的死信队列示例对很多讨论进行了说明。

死信队列

死信队列的用途是存放无法传递给任何接收方的消息或无法处理的消息。 然后,可从 DLQ 中删除和检查这些消息。 应用程序可能允许用户更正问题并重新提交消息。

从 API 和协议的角度看,DLQ 与任何其他队列都极其相似,不同的是,消息只能通过父实体的死信操作提交。 此外,无法查看生存时间,而且不能将 DLQ 中的消息设为死信。 死信队列完全支持正常操作,例如窥视锁定传递、接收和删除和事务操作。

DLQ 并没有自动清理机制。 消息将保留在 DLQ 中,直到您从 DLQ 中显式检索它们并处理完死信信息为止。

死信队列的路径

每个队列和每个订阅都有其自己的死信子队列。 可以使用以下语法直接对其进行寻址(Azure CLI、REST 和 服务总线 Explorer 等工具均使用此语法):

<queue path>/$deadletterqueue
<topic path>/Subscriptions/<subscription path>/$deadletterqueue

使用Azure.Messaging.ServiceBus.NET库时,不会自行构造此路径。 相反,请在创建接收器时将 ServiceBusReceiverOptions.SubQueue 设置为 SubQueue.DeadLetter。 有关示例,请参阅 从死信队列接收消息

DLQ 消息计数

无法获取主题级别死信队列中的消息数,因为消息并不存放在主题级别。 相反,当发送方将消息发送给主题时,消息会在毫秒内转发给主题的订阅,因此消息不再驻留于主题级别。 因此,你可以在与主题订阅相关的 DLQ 中查看消息。 在以下示例中,服务总线资源管理器显示订阅的 DLQ 中当前有 62 条消息:test1。

test1 订阅的死信队列中有 62 条消息。

还可以使用 Azure CLI 命令az servicebus topic subscription show获取 DLQ 消息计数。

将消息移到 DLQ

服务总线中有几个活动会导致从消息引擎本身将消息推送到 DLQ。 应用程序也可以显式将消息移到 DLQ。 以下两个属性(死信原因和死信说明)将添加到死信消息中。 应用程序可以为死信原因属性定义自定义代码,但系统会设置以下值。

死信原因 死信错误说明
HeaderSizeExceeded 此流的大小配额超出了限制。
TTLExpiredException 该消息已过期,并已转入死信队列。 有关详细信息,请参阅生存时间部分。
Session ID is null 支持会话的实体不允许会话标识符为 null 的消息。
MaxTransferHopCountExceeded 在队列之间转发时允许的最大跃点数已超出限制。 此值设为 4。
MaxDeliveryCountExceeded 在最大传送尝试次数以后,仍然无法使用消息。 有关详细信息,请参阅最大传送数部分。

生存时间

在队列或订阅上启用死信时,所有过期的消息都会被移至 DLQ。 消息的死信原因代码设置为 TTLExpiredException。 延迟的消息在过期后不会被清除,也不会被移动到死信队列。 此行为是设计使然。

最大传送数

向 服务总线 队列和订阅传递消息的尝试次数是有限的。 默认值为 10。 每当消息在查看锁定模式下被传递,但随后被显式放弃或锁已过期时,该消息的传递计数都会递增。 如果传送数超出限制,会将消息移至 DLQ。 DLQ 中消息的死信原因设置为 MaxDeliveryCountExceeded。 无法禁止此行为,但可将最大传送数设置为较大的数。

处理订阅规则时的错误

如果您在筛选器评估异常时启用死信队列,则在执行订阅的 SQL 筛选器规则时出现的任何错误以及出错的消息都会被捕获到 DLQ 中。 不要在包含发送到主题(没有订阅者)的消息类型的生产环境中使用此选项,因为它可能会产生大量 DLQ 消息负载。 因此,请确保发送到主题的所有消息至少具有一个匹配的订阅。

应用程序级死信

除了系统提供的死信功能外,应用程序也可以使用 DLQ 显式拒绝无法接受的消息。 不可接受的消息可能包括以下几类:由于任何系统问题而无法被正确处理的消息、包含格式错误负载的消息,或者在采用某种消息级安全方案时未能通过身份验证的消息。

在.NET中,调用 ServiceBusReceiver.DeadLetterMessageAsync 方法

建议在DeadLetterReason中包含异常的类型,在DeadLetterDescription中包含异常的堆栈跟踪,因为这样可以更轻松地排查导致消息死信的问题原因。 这可能会导致某些消息超出标准层Azure 服务总线的 256 KB 配额限制。 可以将服务总线命名空间从标准层升级到高级层以具有更高的配额和限制

从死信队列接收消息

若要使用 Azure.Messaging.ServiceBus .NET 库接收死信消息,请在创建接收器时将 ServiceBusReceiverOptions.SubQueue 设置为 SubQueue.DeadLetter。 该库会为你处理对死信子队列的寻址。

using Azure.Identity;
using Azure.Messaging.ServiceBus;

string fullyQualifiedNamespace = "<NAMESPACE-NAME>.servicebus.chinacloudapi.cn";
string queueName = "<QUEUE-NAME>";

// 1. Create the top-level client. Passwordless authentication is recommended.
await using var client = new ServiceBusClient(fullyQualifiedNamespace, new DefaultAzureCredential());

// 2. Configure options to target the dead-letter sub-queue.
var options = new ServiceBusReceiverOptions
{
    SubQueue = SubQueue.DeadLetter
};

// 3. Create a receiver scoped to the dead-letter queue. For a subscription's
//    dead-letter queue, use: client.CreateReceiver(topicName, subscriptionName, options).
ServiceBusReceiver dlqReceiver = client.CreateReceiver(queueName, options);

// 4. Receive a dead-lettered message.
ServiceBusReceivedMessage dlqMessage = await dlqReceiver.ReceiveMessageAsync();

// 5. Inspect why the message was dead-lettered.
string reason = dlqMessage.DeadLetterReason;
string description = dlqMessage.DeadLetterErrorDescription;

// 6. Complete the message to remove it from the dead-letter queue.
await dlqReceiver.CompleteMessageAsync(dlqMessage);

通过使用 SubQueue.TransferDeadLetter,同样的模式也适用于传输死信队列。

自动转发场景中的死信处理

在下列情况下,消息会发送到死信队列:

  • 消息经过超过 4 个链接在一起的队列或主题。
  • 目标队列或主题被禁用或删除。
  • 目标队列或主题已超过最大实体大小。

通过发送途径场景中的死信处理

  • 如果目标队列或主题已禁用,则会将消息发送到源队列的传输死信队列(TDLQ)。
  • 如果目标队列或目标实体超过其大小限制,消息将被发送到源队列的 TDLQ。

将死信消息发送以便重新处理

解决导致消息进入死信队列的问题后,可以将该消息重新提交到队列或主题进行重新处理。 最简单的方法是在 Azure 门户中使用 服务总线 Explorer,这样就可以在死信队列中查看消息、根据需要编辑其内容或属性,并逐个或分批重新发送消息。 操作人员通常更喜欢这个界面,因为它能显示哪些消息类型失败了、失败来自哪些源实体以及失败原因,同时仍支持批量重新提交。

请参阅为队列或订阅启用死信,了解配置邮件到期时的死信设置的不同方式。