【问题标题】:Is it possible to lose messages using MSMQ MessageQueue.Peek with a timeout?是否有可能使用 MSMQ MessageQueue.Peek 超时丢失消息?
【发布时间】:2010-12-26 11:53:39
【问题描述】:

我目前遇到了丢失消息的问题。此错误很少发生,但经常发生足以令人讨厌。以下是问题的背景:

  • 我已打开 goldmine_service_queue 上的消息日志,这是 Windows 2003 服务器上的一个 MSMQ。
  • 我可以证明该消息已插入到 goldmine_service_queue,因为该消息出现在消息日志中。此信息提供有关消息何时消失的时间信息。
  • 日志记录函数使用http://logging.apache.org/log4net/index.html
  • 日志不显示错误。
  • 工作函数(如下所示)在 Windows 服务的线程中执行。它负责从队列中查看消息(工作项)并进行处理。
  • 从日志中,我强烈怀疑我的问题可能与 MessageQueue.Peek 和超时行为有关。

超时和消息接收是否可能同时发生?是否有更好的方法来处理服务停止检查以帮助避免此错误?

        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();

    }

【问题讨论】:

    标签: c# windows-services msmq


    【解决方案1】:

    这里只是一些 cmets 来阐明 MSMQ 是如何工作的。

    “我可以证明该消息正在被插入到 goldmine_service_queue,因为消息出现在消息日志中。

    当原始消息从 goldmine_service_queue 中删除时,该消息将进入日志队列。所以可以说消息已成功投递到队列并成功从队列中移除。

    “我强烈怀疑我的问题可能与 MessageQueue.Peek 和超时行为有关。”

    Peek 不会从队列中删除消息。只有“q.Receive();”这样做。在您的代码中,被窥视的消息与接收的消息之间没有明确的联系。 “q.接收();”只是说“从队列顶部接收消息”。在多线程环境中,您可能会看到消息的读取不一致 - 有些消息可能会被多次查看和处理。您应该获取 Peeked 消息的 ID 并使用 ReceiveByID,以便您只能接收 peeked 消息。

    【讨论】:

      【解决方案2】:

      我愿意将currentMessage = q.Peek(new TimeSpan(0,0,30)); 更改为currentMessage = q.Receive(); 将解决您的问题。我一直在使用 MSMQ 在完全相同的上下文中传递消息,但只使用 peek(并期望超时异常)来确定它们的队列是否为空。对Receive 的调用被阻塞,因此请相应地进行计划。

      编辑:也用于您的异常捕获 - 您可以通过比较错误代码来检查您的异常是否为超时异常。

      mqe.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout
      

      mqe 是您的消息队列异常。您可能会比字符串比较更喜欢它。

      【讨论】:

      • 我使用Message rsp = colaStatus.ReceiveByCorrelationId(correlationId, new TimeSpan(0, timeOut, 0));。如果 5 分钟的超时时间非常高,我很困惑。恕我直言,我认为最好低于 1 分钟。
      【解决方案3】:

      我认为根据快速审查不应遗漏任何消息,但您的工作方式非常奇怪,竞争条件的范围很大。

      为什么不直接接收消息并将其传递给ProcessMessage(如果ProcessMessage 失败,您仍然在执行读取)。如果您需要处理多个接收者,则在 MSMQ 事务中进行接收,这样消息对其他接收者不可用,但在事务提交之前不会从队列中删除。

      另外,与其轮询队列,不如进行异步接收并让线程池处理完成(您必须调用EndReceive)。这样就节省了线程占用,也不需要特殊情况下服务关闭(关闭消息队列再调用MessageQueue.ClearConnectionCache();)。

      另外,中止线程是一种非常糟糕的退出方式,只需从线程的启动函数返回即可。

      【讨论】:

      • 感谢您的回复: - 关于比赛条件:我不确定我是否看到您提到的比赛条件。根据msdn.microsoft.com/en-us/library/t5te2tk0.aspx,MessageQueue.Peek(Timeout) 是一个阻塞调用。 - 我担心使用异步模式,因为 ProcessMessage 执行一些 COM 操作。我不知道这些操作是否是线程安全的。我可以尝试使用 Receive() 而不是 peak ,但我不知道如何创造机会由于服务停止而停止 while 循环。
      • 回复:ProcessMessage 中的 COM:如果只有一个线程在运行 ProcessMessage,这应该不是问题(这可以在异步情况下通过仅在 ProcessMessage 之后启动新的异步接收来实现已完成。在异步情况下,您关闭队列(并刷新缓存 --- 基于一些搜索,这似乎是真正关闭连接所必需的)并且任何待处理的接收都将被取消。没有要取消的循环。
      猜你喜欢
      • 1970-01-01
      • 2017-06-29
      • 2019-07-07
      • 1970-01-01
      • 2010-11-27
      • 2015-06-12
      • 2017-07-27
      • 1970-01-01
      • 2012-01-17
      相关资源
      最近更新 更多