【问题标题】:MSMQ poison message not found in queue在队列中找不到 MSMQ 毒消息
【发布时间】:2017-11-11 05:35:23
【问题描述】:

遇到了一个奇怪的问题,即 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;
}

这段代码基本上是从微软的示例代码here复制过来的。

抛出的错误类型正确:

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

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

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

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

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

【问题讨论】:

  • 如果这已经在 prod 中工作了一段时间,正如你所说,我会关注配置而不是代码。 environment.machinename 或 appsettings.queuename 是否有可能被更改?某种补丁,或错误配置的部署?
  • @GregHNZ 感谢您的意见。配置是我检查的第一件事之一,队列名称和机器名称都没有改变。我们的托管服务提供商表示,在我们开始出现问题的那天没有进行任何部署或修补。
  • 出现此问题后是否继续检测有害邮件?如果不是,则表明毒消息确实已从队列中消失(例如,因为接收时间已过期?)如果您继续检测它,则表明 LookupId 可能是错误的。无论哪种方式,您都可以将 LookupId 添加到您的 catch 块内的 Logger.Debug 调用中。
  • 是的,毒邮件不断被检测到,具有相同的 id。我不确定 LookupId 应该是什么样子,但它们看起来像这样:504403158270076269

标签: c# .net msmq


【解决方案1】:

当您指定了多个重试周期时,就会发生这种情况。如果您的 maxRetryCycles 大于零并且您的 retryCycleDelay 大于 30 秒,您将看到您描述的问题。该消息实际上位于一个称为“重试”的子队列中,因为它等待周期之间的 retryCycleDelay。因此,当您的 IErrorHandler 在“主”队列中查找它时,它不会找到它。出于某种原因,WCF 在每个重试周期结束时抛出 MsmqPoisonMessageException,而不仅仅是在所有重试周期结束时抛出一次。这意味着您的 IErrorHandler 将在每个周期结束时被调用。对我来说似乎真的很奇怪,但事实就是如此。

现在更好的方法(如果您可以保证您的代码将具有 MSMQ 4.0)是将您的 receiveErrorHandling 从“Fault”更改为“Move”,然后摆脱您的 IErrorHandler。使用这种方法,将在所有重试和重试周期完成后为您移动消息。它被移动到一个名为“poison”的子队列中。

更多详情请看这里:

Poison Message Handling in MSMQ 4.0

【讨论】:

    猜你喜欢
    • 2015-06-21
    • 1970-01-01
    • 1970-01-01
    • 2013-07-02
    • 1970-01-01
    • 2011-05-01
    • 2012-02-12
    • 2012-03-27
    • 1970-01-01
    相关资源
    最近更新 更多