2009-11-25 100 views
12

我从前段时间原来的问题是MSMQ Slow Queue Reading,但是我已经远离了先进的,现在想想我知道这个问题更清晰的一点。MSMQ接收()方法超时

我的代码(以及实际上我使用一个开源库的一部分)看起来是这样的:

queue.Receive(TimeSpan.FromSeconds(10), MessageQueueTransactionType.Automatic); 

它是使用Messaging.MessageQueue.Receive功能和队列是的MessageQueue。问题如下。

上述代码行将被调用指定的超时(10秒)。 Receive(...)函数是一个阻塞函数,并且应该阻塞,直到消息到达队列中并返回。如果在超时命中之前没有收到消息,它将在超时时返回。如果调用该函数时消息在队列中,它将立即返回该消息。

但是,发生的情况是Receive(...)函数被调用,看到队列中没有消息,因此正在等待新消息进入。当新消息进入(在超时之前)时,它没有检测到这个新消息并继续等待。超时最终被击中,此时代码会继续,并再次呼叫Receive(...),它在此处拾取消息并对其进行处理。

现在,这个问题只发生在几天/周之后。我可以通过删除&来重新创建队列。它发生在不同的计算机和不同的队列中。所以看起来似乎有些东西正在建立,直到某些时候它破坏了Receive(...)函数使用的触发/通知功能。

我检查了很多不同的东西,看起来一切都正常&与正常工作的队列没有什么不同。有足够的磁盘空间(13gig免费)和内存(大约350MB从我所知道的1GB中释放出来)。我查看了与其他队列相同的注册表项,性能监视器没有显示任何超出正常范围的内容。我也运行了TMQ工具,因此看不到任何明显的错误。

我在所有机器上都使用Windows XP,并且它们都安装了Service Pack 3。我不会向队列发送大量消息,最多每2秒会发送1条消息,但通常比这个频率少得多。这些消息只有很少,并且远不及4MB的限制。

我刚才注意到的唯一的事情是C:\ WINDOWS \ system32 \ msmq \ storage中的p0000001.mq和r0000067.mq文件都是4,096KB,但是它们在当前没有遇到的其他计算机上的大小问题。这个问题不会发生在计算机上的每个队列上,因为我可以在计算机上重新创建1个问题队列,而其他队列仍然会遇到问题。

我不是非常有经验的MSMQ因此,如果您发布可能需要检查的事项可以请你解释如何检查它们或在哪里可以找到你在说些什么的更多细节。

目前的情况是:

  • ComputerA - 4个队列正常
  • ComputerB - 2个队列遇到问题,1个队列正常
  • ComputerC - 2个队列遇到问题
  • ComputerD - 1队列正常
  • ComputerE - 2个队列正常

所以我有大量的计算机/队列进行比较和测试。

+0

什么是使用的开源库?这是一个已知的问题吗? – 2009-11-25 22:38:46

+0

这是NServiceBus(www.nservicebus.com)。似乎没有其他人遇到这个问题(邮件列表中至少没有人)。我对它是如何工作的解释是基于业主如何描述它。 “NServiceBus线程在完成处理前一条消息(或在启动时)后查看消息的队列.eek的常规行为是阻塞,直到消息到达,但NServiceBus限制超时,以允许正常关闭在任何时间点“。我粘贴的代码行是提到的'偷看'。 – mike 2009-11-25 23:14:02

+1

嗯,我不是这个领域的专家,但是看着队列。接收也许第二个参数不是最适合你的情况,可能是移动到“MessageQueueTransactionType.None”(如果你不需要事务功能)会更好。 避免这种情况的第二种方法(在我看来)是将超时功能移出此调用,意味着在此调用中只等待0秒,并在代码中的其他地方进行等待。 我了解这些选项可能并不理想,因为您可能必须更改库或代码的内容。 祝你好运 – 2009-11-28 13:35:52

回答

0

尝试此

公共消息接收(时间跨度超时,光标光标)

重载函数。

要获取MessageQueue的游标,请为该队列调用CreateCursor方法。

当您需要读取不在队列前面的消息时,光标与Peek(TimeSpan,Cursor,PeekAction)和Receive(TimeSpan,光标)等方法一起使用。这包括同步或异步读取消息。游标不需要用来只读取队列中的第一条消息。

在事务内读取消息时,如果事务中止,消息队列不会回滚光标移动。例如,假设有一个包含两条消息A1和A2的队列。如果在事务中删除消息A1,消息队列将光标移动到消息A2。但是,如果事务由于任何原因而中止,则消息A1被插回到队列中,但光标保持指向消息A2。

要关闭游标,请调用关闭。

8

您没有使用事件处理程序来侦听队列的任何特定原因? System.Messaging库允许您将处理程序附加到队列中,而不是,如果我理解您正在做的是正确的,则每10秒循环一次接收。尝试这样的:

class MSMQListener 
{ 
    public void StartListening(string queuePath) 
    { 
     MessageQueue msQueue = new MessageQueue(queuePath); 
     msQueue.ReceiveCompleted += QueueMessageReceived; 
     msQueue.BeginReceive(); 
    } 

    private void QueueMessageReceived(object source, ReceiveCompletedEventArgs args) 
    { 
     MessageQueue msQueue = (MessageQueue)source; 

     //once a message is received, stop receiving 
     Message msMessage = null; 
     msMessage = msQueue.EndReceive(args.AsyncResult); 

     //do something with the message 

     //begin receiving again 
     msQueue.BeginReceive(); 
    } 
} 
+0

主要原因是您无法从事务性队列中异步接收。这个限制可以追溯到[native API](https://msdn.microsoft.com/en-us/library/ms699825(v = vs.85).aspx)。 – acelent 2016-10-03 16:09:24

3

我们也使用NServiceBus和我们的网络内有类似的问题。

基本上MSMQ使用UDP进行两阶段提交。收到消息后,必须确认。在确认之前,它不能在客户端接收,因为接收事务尚未完成。

这是通过在不同的时间不同的事情对我们造成的: - 一旦这是由于分布式事务处理协调器无法防火墙配置不当 机之间的通信 - 我们使用克隆虚拟机,其他时间没有系统预备这使得内部MSMQ ids不是唯一的,它使得它可以接收到一台机器的消息并将消息发送给另一台机器。最终,MSMQ会计算出来,但这需要很长时间。

希望这会有所帮助。

0

如果你想使用一些完全同步,没有事件中,你可以测试这种方法

public object Receive(string path, int millisecondsTimeout) 
{ 
    var mq = new System.Messaging.MessageQueue(path); 
    var asyncResult = mq.BeginReceive(); 
    var handles = new System.Threading.WaitHandle[] { asyncResult.AsyncWaitHandle }; 
    var index = System.Threading.WaitHandle.WaitAny(handles, millisecondsTimeout); 
    if (index == 258) // Timeout 
    { 
     mq.Close(); 
     return null; 
    } 
    var result = mq.EndReceive(asyncResult); 
    return result; 
}