我目前遇到丢失消息的问题。这个错误很少发生,但经常发生,这很烦人。以下是该问题的背景:
超时和消息接收是否可能同时发生?有没有更好的方法来处理服务停止检查以帮助避免此错误?
private void workerFunction()
{
logger.Info("Connecting to queue: " + Settings.Default.goldmine_service_queue);
MessageQueue q = new MessageQueue(Settings.Default.goldmine_service_queue);
q.Formatter = new ActiveXMessageFormatter();
while (serviceStarted)
{
Message currentMessage = null;
try
{
currentMessage = q.Peek(new TimeSpan(0,0,30));
}
catch (System.Messaging.MessageQueueException mqEx)
{
if (mqEx.ToString().Contains("Timeout for the requested operation has expired"))
{
logger.Info("Check for service stop request");
}
else
{
logger.Error("Exception while peeking into MSMQ: " + mqEx.ToString());
}
}
catch (Exception e)
{
logger.Error("Exception while peeking into MSMQ: " + e.ToString());
}
if (currentMessage != null)
{
logger.Info(currentMessage.Body.ToString());
try
{
ProcessMessage(currentMessage);
}
catch (Exception processMessageException)
{
logger.Error("Error in process message: " + processMessageException.ToString());
}
//Remove message from queue.
logger.Info("Message removed from queue.");
q.Receive();
//logPerformance(ref transCount, ref startTime);
}
}//end while
Thread.CurrentThread.Abort();
}
答案 0 :(得分:5)
我不认为任何消息都应该基于快速审查而错过,但是你的工作方式很奇怪,并且有很多竞争条件。
为什么不接收消息并将其传递给ProcessMessage
(如果ProcessMessage
失败,则无论如何都要执行读取)。如果你需要处理多个接收者,那么在MSMQ事务中进行接收,这样消息对其他接收者不可用,但在提交事务之前不会从队列中删除。
此外,不是轮询队列,为什么不进行异步接收并让线程池处理完成(必须调用EndReceive
)。这样可以节省绑定线程,并且您不需要关闭特殊情况服务(关闭消息队列然后调用MessageQueue.ClearConnectionCache();
)。
此外,中止线程是一种非常糟糕的退出方式,只需从线程的启动函数返回。
答案 1 :(得分:3)
只是一些评论,以澄清MSMQ如何在这里工作。
“我可以证明消息正在插入goldmine_service_queue,因为消息显示在消息日志中。
当从goldmine_service_queue中删除原始邮件时,邮件将进入日记帐队列。因此,您可以说消息已成功传递到队列并成功从队列中删除。
“我强烈怀疑我的问题可能与MessageQueue.Peek和超时行为有关。”
A Peek不会从队列中删除消息。只有“q.Receive();”那样做。在您的代码中,被查看的消息与正在接收的消息之间没有明确的连接。 “q.Receive();”只是说“从队列顶部接收消息”。在多线程环境中,您可能会看到消息读取不一致 - 有些可能会被偷看并多次处理。您应该获取Peeked消息的ID并使用ReceiveByID,这样您才能收到偷看的消息。
答案 2 :(得分:1)
我愿意将currentMessage = q.Peek(new TimeSpan(0,0,30));
更改为currentMessage = q.Receive();
来解决您的问题。我一直在完全相同的上下文中使用MSMQ进行消息传递,但只使用peek(并期望超时异常)来确定它们的队列是否为空。对Receive
的调用是阻塞的,因此请做出相应的计划。
编辑:也用于异常捕获 - 您可以通过比较错误代码来检查您的异常是否是超时异常。
mqe.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout
其中mqe是您的消息队列异常。你可能比字符串比较更喜欢它。