服务总线死信队列概述Overview of Service Bus dead-letter queues

Azure 服务总线队列和主题订阅提供一个名为“死信队列 (DLQ)”的辅助子队列。Azure Service Bus queues and topic subscriptions provide a secondary sub-queue, called a dead-letter queue (DLQ). 死信队列不需要显式创建,并且不能删除或以其他方式独立于主实体进行管理。The dead-letter queue does not need to be explicitly created and cannot be deleted or otherwise managed independent of the main entity.

本文介绍服务总线中的死信队列。This article describes dead-letter queues in Service Bus. GitHub 上的死信队列示例对很多讨论进行了说明。Much of the discussion is illustrated by the Dead-Letter queues sample on GitHub.

死信队列The dead-letter queue

死信队列的用途是存放无法传递给任何接收方的消息或存放无法处理的消息。The purpose of the dead-letter queue is to hold messages that cannot be delivered to any receiver, or messages that could not be processed. 然后,可从 DLQ 中删除和检查这些消息。Messages can then be removed from the DLQ and inspected. 应用程序可能会在操作员的帮助下,更正问题并重新提交消息,记录出错的实际情况和执行更正操作。An application might, with help of an operator, correct issues and resubmit the message, log the fact that there was an error, and take corrective action.

从 API 和协议的角度看,DLQ 与任何其他队列都极其相似,不同的是,消息只能通过父实体的死信操作提交。From an API and protocol perspective, the DLQ is mostly similar to any other queue, except that messages can only be submitted via the dead-letter operation of the parent entity. 此外,无法查看生存时间,而且不能将 DLQ 中的消息设为死信。In addition, time-to-live is not observed, and you can't dead-letter a message from a DLQ. 死信队列完全支持扫视锁定传递和事务性操作。The dead-letter queue fully supports peek-lock delivery and transactional operations.

请注意,DLQ 不自动执行清理。Note that there is no automatic cleanup of the DLQ. 消息将保留在 DLQ 中,直到显式从 DLQ 中检索它们以及对死信消息调用 Complete() 为止。Messages remain in the DLQ until you explicitly retrieve them from the DLQ and call Complete() on the dead-letter message.

将消息移到 DLQMoving messages to the DLQ

服务总线中有几个活动会导致从消息引擎本身将消息推送到 DLQ。There are several activities in Service Bus that cause messages to get pushed to the DLQ from within the messaging engine itself. 应用程序也可以显式将消息移到 DLQ。An application can also explicitly move messages to the DLQ.

如果消息是由代理移动的,在代理对消息调用其内部版本的 DeadLetter 方法时,会将两个属性添加到消息:DeadLetterReasonDeadLetterErrorDescriptionAs the message gets moved by the broker, two properties are added to the message as the broker calls its internal version of the DeadLetter method on the message: DeadLetterReason and DeadLetterErrorDescription.

应用程序可以为 DeadLetterReason 属性定义自己的代码,但系统设置以下值。Applications can define their own codes for the DeadLetterReason property, but the system sets the following values.

条件Condition DeadLetterReasonDeadLetterReason DeadLetterErrorDescriptionDeadLetterErrorDescription
AlwaysAlways HeaderSizeExceededHeaderSizeExceeded 已超过此流的大小配额。The size quota for this stream has been exceeded.
!TopicDescription。!TopicDescription.
EnableFilteringMessagesBeforePublishing 和 SubscriptionDescription。EnableFilteringMessagesBeforePublishing and SubscriptionDescription.
EnableDeadLetteringOnFilterEvaluationExceptionsEnableDeadLetteringOnFilterEvaluationExceptions
exception.GetType().Nameexception.GetType().Name exception.Messageexception.Message
EnableDeadLetteringOnMessageExpirationEnableDeadLetteringOnMessageExpiration TTLExpiredExceptionTTLExpiredException 消息已过期并已设为死信。The message expired and was dead lettered.
SubscriptionDescription.RequiresSessionSubscriptionDescription.RequiresSession 会话 ID 为 null。Session id is null. 启用会话的实体不允许使用会话标识符为 null 的消息。Session enabled entity doesn't allow a message whose session identifier is null.
!死信队列!dead letter queue MaxTransferHopCountExceededMaxTransferHopCountExceeded NullNull
应用程序显式设为死信Application explicit dead lettering 由应用程序指定Specified by application 由应用程序指定Specified by application

超过 MaxDeliveryCountExceeding MaxDeliveryCount

每个队列和订阅都具有 QueueDescription.MaxDeliveryCountSubscriptionDescription.MaxDeliveryCount 属性;默认值为 10。Queues and subscriptions each have a QueueDescription.MaxDeliveryCount and SubscriptionDescription.MaxDeliveryCount property respectively; the default value is 10. 只要消息在 (ReceiveMode.PeekLock) 锁下传递,但已显式放弃或锁已过期,消息 BrokeredMessage.DeliveryCount 就会递增。Whenever a message has been delivered under a lock (ReceiveMode.PeekLock), but has been either explicitly abandoned or the lock has expired, the message BrokeredMessage.DeliveryCount is incremented. DeliveryCount 超过 MaxDeliveryCount 时,该消息将移到 DLQ,并指定 MaxDeliveryCountExceeded 原因代码。When DeliveryCount exceeds MaxDeliveryCount, the message is moved to the DLQ, specifying the MaxDeliveryCountExceeded reason code.

无法禁止此行为,但可以将 MaxDeliveryCount 设置为非常大的数。This behavior cannot be disabled, but you can set MaxDeliveryCount to a very large number.

超过 TimeToLiveExceeding TimeToLive

QueueDescription.EnableDeadLetteringOnMessageExpirationSubscriptionDescription.EnableDeadLetteringOnMessageExpiration 属性设置为 true(默认值是 false)时,所有到期的消息将移到 DLQ,并指定 TTLExpiredException 原因代码。When the QueueDescription.EnableDeadLetteringOnMessageExpiration or SubscriptionDescription.EnableDeadLetteringOnMessageExpiration property is set to true (the default is false), all expiring messages are moved to the DLQ, specifying the TTLExpiredException reason code.

请注意,如果主队列或订阅中至少有一个活动的接收器正在拉取,则会清除过期的消息,该消息会被移动到 DLQ;该行为是设计使然。Note that expired messages are only purged and moved to the DLQ when there is at least one active receiver pulling from the main queue or subscription; that behavior is by design.

处理订阅规则时出错Errors while processing subscription rules

当为订阅启用了 SubscriptionDescription.EnableDeadLetteringOnFilterEvaluationExceptions 属性时,会在 DLQ 中捕获执行订阅的 SQL 筛选器规则时出现的任何错误以及有问题的消息。When the SubscriptionDescription.EnableDeadLetteringOnFilterEvaluationExceptions property is enabled for a subscription, any errors that occur while a subscription's SQL filter rule executes are captured in the DLQ along with the offending message.

应用程序级死信Application-level dead-lettering

除了系统提供的死信功能外,应用程序也可以使用 DLQ 显式拒绝无法接受的消息。In addition to the system-provided dead-lettering features, applications can use the DLQ to explicitly reject unacceptable messages. 这可能包括由于任何类型的系统问题而导致无法正确处理的消息、存储格式错误的有效负载的消息或在使用某种消息级安全方案时未通过身份验证的消息。This can include messages that cannot be properly processed due to any sort of system issue, messages that hold malformed payloads, or messages that fail authentication when some message-level security scheme is used.

ForwardTo 或 SendVia 方案中的死信Dead-lettering in ForwardTo or SendVia scenarios

在下列情况下,消息将发送到传输死信队列:Messages will be sent to the transfer dead-letter queue under the following conditions:

  • 消息传递 4 个以上链接在一起的队列或主题。A message passes through more than 4 queues or topics that are chained together.
  • 目标队列或主题被禁用或删除。The destination queue or topic is disabled or deleted.
  • 目标队列或主题超出最大实体大小。The destination queue or topic exceeds the maximum entity size.

若要检索这些死信消息,可以使用 FormatTransferDeadletterPath 实用方法创建接收器。To retrieve these dead-lettered messages, you can create a receiver using the FormatTransferDeadletterPath utility method.

示例Example

下面的代码片段会创建一个消息接收器。The following code snippet creates a message receiver. 在主队列的接收循环中,此代码使用 Receive(TimeSpan.Zero) 检索消息,该方法请求代理立即返回随时可用的任何消息或返回空结果。In the receive loop for the main queue, the code retrieves the message with Receive(TimeSpan.Zero), which asks the broker to instantly return any message readily available, or to return with no result. 如果此代码接收到一条消息,则会立即将其放弃,从而使 DeliveryCount 递增。If the code receives a message, it immediately abandons it, which increments the DeliveryCount. 系统将此消息移动到 DLQ 后,ReceiveAsync 返回 null,主队列为空,且循环退出。Once the system moves the message to the DLQ, the main queue is empty and the loop exits, as ReceiveAsync returns null.

var receiver = await receiverFactory.CreateMessageReceiverAsync(queueName, ReceiveMode.PeekLock);
while(true)
{
    var msg = await receiver.ReceiveAsync(TimeSpan.Zero);
    if (msg != null)
    {
        Console.WriteLine("Picked up message; DeliveryCount {0}", msg.DeliveryCount);
        await msg.AbandonAsync();
    }
    else
    {
        break;
    }
}

死信队列的路径Path to the dead-letter queue

可以使用以下语法访问死信队列:You can access the dead-letter queue by using the following syntax:

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

如果使用的是 .NET SDK,则可以通过使用 SubscriptionClient.FormatDeadLetterPath() 方法获取死信队列的路径。If you are using the .NET SDK, you can get the path to the dead-letter queue by using the SubscriptionClient.FormatDeadLetterPath() method. 此方法使用 /$DeadLetterQueue 接受主题名称/订阅名称和后缀。This method takes the topic name/subscription name and suffixes with /$DeadLetterQueue.

后续步骤Next steps

有关服务总线队列的详细信息,请参阅以下文章:See the following articles for more information about Service Bus queues: