【发布时间】: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