队列中找不到MSMQ中毒消息

时间:2017-06-09 09:07:20

标签: c# .net msmq

在MSMQ队列中遇到了有毒消息的奇怪问题。当检测到有毒消息时,我正在使用下面的代码来处理异常并将消息移动到毒性队列,但是这会失败,因为即使我从抛出的异常中获取了lookupId,也找不到消息。请参阅下面的相关代码。

public bool HandleError(Exception error)
{
    var poisonException = error as MsmqPoisonMessageException;
    if (null == poisonException) return false;

    var lookupId = poisonException.MessageLookupId;

    var queuePath = Environment.MachineName + "\\" + ConfigurationManager.AppSettings["QueuePath"];
    var poisonQueuePath = Environment.MachineName + "\\" + ConfigurationManager.AppSettings["PoisonQueuePath"];

    var orderQueue = new System.Messaging.MessageQueue(queuePath);
    var poisonMessageQueue = new System.Messaging.MessageQueue(poisonQueuePath);

    // Use a new transaction scope to remove the message from the main queue and add it to the poison queue.
    using (var txScope = new TransactionScope(TransactionScopeOption.RequiresNew))
    {
        int retryCount = 0;
        while (retryCount < 3)
        {
            retryCount++;

            try
            {
                // Try to get the poison message using the look up id. This line throws InvalidOperationException
                var message = orderQueue.ReceiveByLookupId(lookupId);
                // Send the message to the poison message queue.
                poisonMessageQueue.Send(message, System.Messaging.MessageQueueTransactionType.Automatic);

                txScope.Complete();

                Logger.Debug("Moved poisoned message with look up id: " + lookupId + " to poison queue: " + ConfigurationManager.AppSettings["PoisonQueuePath"]);
                break;
            }
            catch (InvalidOperationException e)
            {
                if (retryCount < 3)
                {
                    Logger.Debug("Trying to move message to poison queue but message is not available, sleeping for 10 seconds before retrying", e);
                    Thread.Sleep(TimeSpan.FromSeconds(10));
                }
                else
                {
                    Logger.Debug("Giving up on trying to move the message", e);
                }
            }
        }
    }

    Logger.Info("Restarting the service to process rest of the messages in the queue");
    WaitCallback restartCallback = new WaitCallback(Start);
    ThreadPool.QueueUserWorkItem(restartCallback);

    return true;
}

此代码基本上是从Microsoft的示例代码here复制而来。

抛出的错误类型正确:

System.ServiceModel.MsmqPoisonMessageException: The transport channel detected a poison message.

但是当我尝试从队列中获取消息时,我得到了:

System.InvalidOperationException: Message requested was not found in the queue specified.

我的第一个想法是队列可能没有设置正确的权限,但我已经仔细检查过网络服务用户是否具有读取和写入两个队列消息的所有必要权限。

值得一提的是,这段代码已经在生产中完美地运行了几个月,并且在过去的许多有毒消息中幸存下来。关于可能导致此问题的任何意见都非常感谢。

1 个答案:

答案 0 :(得分:2)

如果指定了多个重试周期,则会发生这种情况。如果您的maxRetryCycles大于零且retryCycleDelay大于30秒,您将看到您描述的问题。该消息实际上位于一个名为&#34; retry&#34;的子队列中。因为它在循环之间等待retryCycleDelay。所以当你的IErrorHandler在&#34; main&#34;中找到它时排队,它不会找到它。由于某种原因,WCF在每个重试周期结束时抛出MsmqPoisonMessageException,而不是在所有重试周期结束时抛出一次。这意味着您的IErrorHandler将在每个周期结束时被调用。对我来说似乎很奇怪,但事情就是这样。

现在更好的方法(如果你可以保证你的代码将具有MSMQ 4.0)是改变你的receiveErrorHandling来自&#34; Fault&#34;到&#34;移动&#34;然后摆脱你的IErrorHandler。通过这种方法,在完成所有重试和重试周期后,将为您移动消息。它被移动到一个名为&#34; poison&#34;。

的子队列

请点击此处了解更多详情:

Poison Message Handling in MSMQ 4.0