从死信队列重新提交邮件 - Azure Service Bus

时间:2017-01-23 02:55:28

标签: azure message-queue azureservicebus

我在Azure中创建了一个服务总线队列并且运行良好。如果消息未在默认尝试(10次)内传递,则正确地将消息移动到死信队列。

现在,我想将此消息从死信队列重新提交回其发起的队列,并查看它是否再次起作用。我尝试过使用服务总线资源管理器。但它会立即转移到死信队列。

是否可以这样做,如果是这样的话怎么样?

7 个答案:

答案 0 :(得分:4)

您需要发送具有相同有效负载的新消息。 ASB设计不支持重新提交消息。

答案 1 :(得分:2)

当您修复并从deadletter队列重新提交邮件时,Service Bus Explorer工具始终会创建原始邮件的克隆。它可能没有任何不同,因为默认情况下,Service Bus消息传递不提供任何消息修复和重新提交机制。我建议你研究一下为什么你的消息最终会在deadletter队列中以及你重新提交它时克隆它。希望这有帮助!

答案 2 :(得分:2)

听起来它可能与ASB的“重复邮件检测”功能有关。

当您在ServiceBus资源管理器中重新提交邮件时,它将克隆邮件,从而新邮件将与deadletter队列中的原始邮件具有相同的ID。

如果您已在队列/主题上启用“需要重复检测”,并且您尝试在“重复检测历史记录时间窗口”内重新提交该消息,则该消息将立即再次移至deadletter队列。

如果您想使用Service Bus Explorer重新提交死信消息,那么我认为您必须在队列/主题上禁用“需要重复检测”。

答案 3 :(得分:2)

可能是“重复邮件检测”,如Peter Berggreen所示,或者如果您直接将BrokeredMessage从死信队列移动到实时队列,那么DeliveryCount仍然处于最大值并且它将返回到死信队列。

将BrokeredMessage从死信队列中拉出,使用GetBody()获取内容,使用该数据在新的BrokeredMessage中创建并将其发送到队列。您可以在安全的庄园中执行此操作,方法是使用peek将消息内容从死信队列中删除,然后在从死信队列中删除消息之前将新消息发送到实时队列。这样,如果由于某种原因它无法写入实时队列,您将不会丢失任何关键数据。

使用新的BrokeredMessage,您不应该遇到“重复邮件检测”的问题,并且DeliveryCount将重置为零。

答案 4 :(得分:1)

尝试消除不受欢迎的原因

resubmittableMessage.Properties.Remove("DeadLetterReason");
resubmittableMessage.Properties.Remove("DeadLetterErrorDescription");

完整代码

using Microsoft.ServiceBus.Messaging;
using System.Transactions;

namespace ResubmitDeadQueue
{
    class Program
    {
        static void Main(string[] args)
        {

            var connectionString = "";
            var queueName = "";

            var queue = QueueClient.CreateFromConnectionString(connectionString, QueueClient.FormatDeadLetterPath(queueName), ReceiveMode.PeekLock);

            BrokeredMessage originalMessage
                ;
            var client = QueueClient.CreateFromConnectionString(connectionString, queueName);
            do
            {
                originalMessage = queue.Receive();
                if (originalMessage != null)
                {
                    using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        // Create new message
                        var resubmittableMessage = originalMessage.Clone();

                        // Remove dead letter reason and description
                        resubmittableMessage.Properties.Remove("DeadLetterReason");
                        resubmittableMessage.Properties.Remove("DeadLetterErrorDescription");

                        // Resend cloned DLQ message and complete original DLQ message
                        client.Send(resubmittableMessage);
                        originalMessage.Complete();

                        // Complete transaction
                        scope.Complete();
                    }
                }
            } while (originalMessage != null);
        }
    }
}

在这里感谢其他回应!

答案 5 :(得分:1)

我们经常需要重新提交消息。 @Baglay-Vyacheslav 的回答很有帮助。我粘贴了一些适用于最新 Azure.Messaging.ServiceBus Nuget 包的更新 C# 代码。

使处理两个队列/主题/订阅者上的 DLQ 变得更快/更容易。

using Azure.Messaging.ServiceBus;
using System.Collections.Generic;
using System.Threading.Tasks;
using NLog;

namespace ServiceBus.Tools
{
    class TransferDeadLetterMessages
    {
        // https://github.com/Azure/azure-sdk-for-net/blob/Azure.Messaging.ServiceBus_7.2.1/sdk/servicebus/Azure.Messaging.ServiceBus/README.md

        private static Logger logger = LogManager.GetCurrentClassLogger();

        private static ServiceBusClient client;
        private static ServiceBusSender sender;
    
        public static async Task ProcessTopicAsync(string connectionString, string topicName, string subscriberName, int fetchCount = 10)
        {
            try
            {
                client = new ServiceBusClient(connectionString);
                sender = client.CreateSender(topicName);

                ServiceBusReceiver dlqReceiver = client.CreateReceiver(topicName, subscriberName, new ServiceBusReceiverOptions
                {
                    SubQueue = SubQueue.DeadLetter,
                    ReceiveMode = ServiceBusReceiveMode.PeekLock
                });

                await ProcessDeadLetterMessagesAsync($"topic: {topicName} -> subscriber: {subscriberName}", fetchCount, sender, dlqReceiver);
            }
            catch (Azure.Messaging.ServiceBus.ServiceBusException ex)
            {
                if (ex.Reason == Azure.Messaging.ServiceBus.ServiceBusFailureReason.MessagingEntityNotFound)
                {
                    logger.Error(ex, $"Topic:Subscriber '{topicName}:{subscriberName}' not found. Check that the name provided is correct.");
                }
                else
                {
                    throw;
                }
            }
            finally
            {
                await sender.CloseAsync();
                await client.DisposeAsync();
            }
        }

        public static async Task ProcessQueueAsync(string connectionString, string queueName, int fetchCount = 10)
        {         
            try
            {
                client = new ServiceBusClient(connectionString);
                sender = client.CreateSender(queueName);

                ServiceBusReceiver dlqReceiver = client.CreateReceiver(queueName, new ServiceBusReceiverOptions
                {
                    SubQueue = SubQueue.DeadLetter,
                    ReceiveMode = ServiceBusReceiveMode.PeekLock
                });

                await ProcessDeadLetterMessagesAsync($"queue: {queueName}", fetchCount, sender, dlqReceiver);
            }
            catch (Azure.Messaging.ServiceBus.ServiceBusException ex)
            {
                if (ex.Reason == Azure.Messaging.ServiceBus.ServiceBusFailureReason.MessagingEntityNotFound)
                {
                    logger.Error(ex, $"Queue '{queueName}' not found. Check that the name provided is correct.");
                }
                else
                {
                    throw;
                }
            }
            finally
            {
                await sender.CloseAsync();
                await client.DisposeAsync();
            }
        }

        private static async Task ProcessDeadLetterMessagesAsync(string source, int fetchCount, ServiceBusSender sender, ServiceBusReceiver dlqReceiver)
        {
            var wait = new System.TimeSpan(0, 0, 10);

            logger.Info($"fetching messages ({wait.TotalSeconds} seconds retrieval timeout)");
            logger.Info(source);

            IReadOnlyList<ServiceBusReceivedMessage> dlqMessages = await dlqReceiver.ReceiveMessagesAsync(fetchCount, wait);

            logger.Info($"dl-count: {dlqMessages.Count}");

            int i = 1;

            foreach (var dlqMessage in dlqMessages)
            {
                logger.Info($"start processing message {i}");
                logger.Info($"dl-message-dead-letter-message-id: {dlqMessage.MessageId}");
                logger.Info($"dl-message-dead-letter-reason: {dlqMessage.DeadLetterReason}");
                logger.Info($"dl-message-dead-letter-error-description: {dlqMessage.DeadLetterErrorDescription}");

                ServiceBusMessage resubmittableMessage = new ServiceBusMessage(dlqMessage);

                await sender.SendMessageAsync(resubmittableMessage);

                await dlqReceiver.CompleteMessageAsync(dlqMessage);

                logger.Info($"finished processing message {i}");
                logger.Info("--------------------------------------------------------------------------------------");

                i++;
            }

            await dlqReceiver.CloseAsync();

            logger.Info($"finished");
        }
    }
}

答案 6 :(得分:0)

我们有大约6万条消息,需要从死信队列中对其进行重新处理。从我的机器每发送1k消息,通过Service Bus Explorer窥视并发送回消息大约需要6分钟。我通过为DLQ消息设置转发规则到另一个队列并从那里将其自动转发到原始队列来解决了这个问题。对于所有6万条消息,此解决方案花费了大约30秒。