在Azure Service Bus上的死信队列中完成一条消息

时间:2015-11-10 00:42:35

标签: azure azureservicebus azure-servicebus-queues

我希望能够从我的死信队列中删除所选邮件。

这是如何完成的?

我经常收到错误:

无法完成操作,因为RecieveContext为Null

我尝试了所有我能想到和阅读的方法,这就是我现在所处的位置:

public void DeleteMessageFromDeadletterQueue<T>(string queueName, long sequenceNumber)
        {
     var client = GetQueueClient(queueName, true);
                var messages = GetMessages(client);
                foreach(var m in messages)
                {
                    if(m.SequenceNumber == sequenceNumber)
                    {
                        m.Complete();
                    }
                    else
                    {
                        m.Abandon();
                    }
                }
}

/// <summary>
        /// Return a list of all messages in a Queue
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        private IEnumerable<BrokeredMessage> GetMessages(QueueClient client)
        {
            var peekedMessages = client.PeekBatch(0, peekedMessageBatchCount).ToList();
            bool getmore = peekedMessages.Count() == peekedMessageBatchCount ? true : false;

            while (getmore)
            {
                var moreMessages = client.PeekBatch(peekedMessages.Last().SequenceNumber, peekedMessageBatchCount);
                peekedMessages.AddRange(moreMessages);
                getmore = moreMessages.Count() == peekedMessageBatchCount ? true : false;
            }

            return peekedMessages;
        }

不确定为什么这似乎是一项艰巨的任务。

2 个答案:

答案 0 :(得分:3)

这里的问题是你调用了PeekBatch,它返回刚刚查看过的消息。没有接收上下文,您可以使用它来完成或放弃该消息。 Peek和PeekBatch操作仅返回消息,并且根本不锁定它们,即使receiveMode设置为PeekLock也是如此。它主要用于浏览队列,但您无法对其进行操作。请注意Abandon和Complete状态的文档,“只能在使用Peek-Lock ReceiveMode中运行的接收器接收到的消息上调用”。这里不太清楚,但Peek和PeekBatch操作不计入其中,因为它们实际上并没有获得接收上下文。这就是为什么当你试图调用放弃时失败的原因。如果您确实找到了您正在寻找的那个,那么当您调用Complete时会抛出不同的错误。

您要做的是在PeekBatch RecieveMode中使用ReceiveBatch操作。这实际上会将一批消息拉回来,然后当您查看它们以找到您想要的消息时,实际上可以影响消息的完成。当您触发放弃时,它将立即释放不是您想要回到队列的消息。

如果您的死信通常队列非常小,通常这也不错。如果它真的很大,那么采用这种方法并不是最有效的。您将死信队列更像堆,并通过它来处理,而不是“按顺序”处理消息。这在处理需要人工干预的死信队列时并不少见,但是如果你有很多这样的话,那么将死​​信队列处理成不同类型的商店可能会更容易找到,销毁消息,但仍然可以重新创建可以推送到不同队列进行重新处理的消息。

可能还有其他选项,例如使用Defer,如果您手动对文字进行刻字。见How to use the MessageReceiver.Receive method by sequenceNumber on ServiceBus

答案 1 :(得分:0)

我对MikeWo的建议没有成功,因为当我使用将DLQ QueueClient与ReceiveMode.PeekLock实例化并使用ReceiveBatch拉取消息的组合时,我正在使用Receive / ReceiveBatch的版本通过其SequenceNumber请求消息。

[另外:在我的应用程序中,我查看所有消息并列出它们,并让另一个处理程序根据特定的序列号重新排队到主队列中的死信息消息...]

但是对DLQClient上的Receive(long sequenceNumber)或ReceiveBatch(IEnumerable sequenceNumber)的调用总是抛出异常,“无法锁定一条或多条指定的消息。消息不存在。” (即使我只通过了1,它肯定在队列中)。

此外,由于原因不明确,使用ReceiveBatch(int messageCount),无论使用什么值作为messageCount,始终只返回队列中的下一条消息。

最终对我有用的是:

QueueClient queueClient, deadLetterClient;
GetQueueClients(qname, ReceiveMode.PeekLock, out queueClient, out deadLetterClient);

BrokeredMessage msg = null;
var mapSequenceNumberToBrokeredMessage = new Dictionary<long, BrokeredMessage>();
while (msg == null)
{
#if UseReceive
    var message = deadLetterClient.Receive();
#elif UseReceiveBatch
    var messageEnumerable = deadLetterClient.ReceiveBatch(CnCountOfMessagesToPeek).ToList();
    if ((messageEnumerable == null) || (messageEnumerable.Count == 0))
        break;
    else if (messageEnumerable.Count != 1)
        throw new ApplicationException("Invalid expectation that there'd always be only 1 msg returned by ReceiveBatch");

    // I've yet to get back more than one in the deadletter queue, but... 
    var message = messageEnumerable.First();
#endif
    if (message.SequenceNumber == lMessageId)
    {
        msg = message;
        break;
    }
    else if (mapSequenceNumberToBrokeredMessage.ContainsKey(message.SequenceNumber))
    {
        // this means it's started the list over, so we didn't find it...
        break;
    }
    else
        mapSequenceNumberToBrokeredMessage.Add(message.SequenceNumber, message);
    message.Abandon();
}                        

if (msg == null)
    throw new ApplicationException("Unable to find a message in the deadletter queue with the SequenceNumber: " + msgid);

var strMessage = GetMessage(msg);
var newMsg = new BrokeredMessage(strMessage);
queueClient.Send(newMsg);

msg.Complete();