我刚才的原始问题是MSMQ Slow Queue Reading,但是我已经提出了这个问题,现在我想我知道这个问题更清楚了。
我的代码(实际上是我正在使用的开源库的一部分)看起来像这样:
queue.Receive(TimeSpan.FromSeconds(10), MessageQueueTransactionType.Automatic);
使用Messaging.MessageQueue.Receive函数和队列的是MessageQueue。问题如下。
将使用指定的超时(10秒)调用上面的代码行。 Receive(...)
函数是一个阻塞函数,应该阻塞,直到消息到达队列,此时它将返回。如果在超时之前没有收到任何消息,它将在超时时返回。如果在调用函数时消息在队列中,它将立即返回该消息。
然而,正在发生的事情是调用Receive(...)
函数,看到队列中没有消息,因此等待新消息进入。当有新消息进入时(在超时),它没有检测到这条新消息并继续等待。最终会超时超时,此时代码会继续并再次调用Receive(...)
,然后它会获取消息并对其进行处理。
现在,此问题仅在数天/周后发生。我可以通过删除&重新创建队列。它发生在不同的计算机和不同的队列上。所以看起来有些事情正在积累,直到它突破了Receive(...)
函数使用的触发/通知能力。
我检查了很多不同的东西,一切看起来都很正常&与正常工作的队列没有区别。有足够的磁盘空间(13gig免费)和RAM(从我可以告诉的1GB大约350MB空闲)。我检查了所有看起来与其他队列相同的注册表项,并且性能监视器没有显示任何异常。我也运行了TMQ工具,看不出任何明显的错误。
我在所有机器上都使用Windows XP,它们都安装了Service Pack 3。我没有向队列发送大量消息,最多每2秒发送一次,但通常不会那么频繁。消息也很小,远不及4MB的限制。
我唯一注意到的是C:\ WINDOWS \ system32 \ msmq \ storage中的p0000001.mq和r0000067.mq文件都是4,096KB但是在其他计算机上它们的大小也是当前没有遇到的问题。问题不会同时发生在计算机上的每个队列中,因为我可以在计算机上重新创建1个问题队列,而其他队列仍然会遇到问题。
我对MSMQ不是很有经验,所以如果您发布可能要检查的内容,请解释如何检查它们或在哪里可以找到有关您所谈论内容的更多详细信息。
目前的情况是:
所以我有大量的计算机/队列来比较和测试。
答案 0 :(得分: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();
}
}
答案 1 :(得分:3)
我们也在使用NServiceBus,并且在我们的网络中遇到了类似的问题。
基本上MSMQ使用UDP进行两阶段提交。收到消息后,必须确认。在确认之前,由于接收交易尚未最终确定,因此无法在客户端收到。
这是由于我们在不同时间的不同事情造成的: - 一旦这是由于分布式事务处理协调器无法在计算机之间进行通信而导致防火墙配置错误 - 另一次我们使用没有sysprep的克隆虚拟机,这使得内部MSMQ ID不是唯一的,并使它接收到一台机器的消息并确认另一台机器。最终MSMQ解决了问题,但需要很长时间。
希望这有帮助。
答案 2 :(得分:0)
试试这个
公共消息接收(TimeSpan超时,光标游标)
重载功能。
要获取MessageQueue的游标,请为该队列调用CreateCursor方法。
当您需要读取不在队列前面的消息时,Cursor与Peek(TimeSpan,Cursor,PeekAction)和Receive(TimeSpan,Cursor)等方法一起使用。这包括同步或异步读取消息。不需要使用游标来只读取队列中的第一条消息。
在事务中读取消息时,如果事务中止,消息队列不会回滚光标移动。例如,假设有一个包含两个消息的队列,A1和A2。如果在事务中删除消息A1,则消息队列将光标移动到消息A2。但是,如果事务因任何原因中止,则将消息A1插回队列,但光标仍然指向消息A2。
要关闭光标,请调用Close。
答案 3 :(得分: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;
}