MSMQ Receive() method timeout
Asked Answered
A

4

13

My original question from a while ago is MSMQ Slow Queue Reading, however I have advanced from that and now think I know the problem a bit more clearer.

My code (well actually part of an open source library I am using) looks like this:

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

Which is using the Messaging.MessageQueue.Receive function and queue is a MessageQueue. The problem is as follows.

The above line of code will be called with the specified timeout (10 seconds). The Receive(...) function is a blocking function, and is supposed to block until a message arrives in the queue at which time it will return. If no message is received before the timeout is hit, it will return at the timeout. If a message is in the queue when the function is called, it will return that message immediately.

However, what is happening is the Receive(...) function is being called, seeing that there is no message in the queue, and hence waiting for a new message to come in. When a new message comes in (before the timeout), it isn't detecting this new message and continues waiting. The timeout is eventually hit, at which point the code continues and calls Receive(...) again, where it picks up the message and processes it.

Now, this problem only occurs after a number of days/weeks. I can make it work normally again by deleting & recreating the queue. It happens on different computers, and different queues. So it seems like something is building up, until some point when it breaks the triggering/notification ability that the Receive(...) function uses.

I've checked a lot of different things, and everything seems normal & isn't different from a queue that is working normally. There is plenty of disk space (13gig free) and RAM (about 350MB free out of 1GB from what I can tell). I have checked registry entries which all appear the same as other queues, and the performance monitor doesn't show anything out of the normal. I have also run the TMQ tool and can't see anything noticably wrong from that.

I am using Windows XP on all the machines and they all have service pack 3 installed. I am not sending a large amount of messages to the queues, at most it would be 1 every 2 seconds but generally a lot less frequent than that. The messages are only small too and nowhere near the 4MB limit.

The only thing I have just noticed is the p0000001.mq and r0000067.mq files in C:\WINDOWS\system32\msmq\storage are both 4,096KB however they are that size on other computers also which are not currently experiencing the problem. The problem does not happen to every queue on the computer at once, as I can recreate 1 problem queue on the computer and the other queues still experience the problem.

I am not very experienced with MSMQ so if you post possible things to check can you please explain how to check them or where I can find more details on what you are talking about.

Currently the situation is:

  • ComputerA - 4 queues normal
  • ComputerB - 2 queues experiencing problem, 1 queue normal
  • ComputerC - 2 queues experiencing problem
  • ComputerD - 1 queue normal
  • ComputerE - 2 queues normal

So I have a large number of computers/queues to compare and test against.

Ashjian answered 25/11, 2009 at 2:33 Comment(8)
What's the open source library used? Is it a known issue on their side?Harley
It is NServiceBus (www.nservicebus.com). It doesn't seem like anyone else experiences the issue (at least nobody on the mailing list is). My explanation of how it works is based on how the owner describes it. "An NServiceBus thread peeks the queue for a message after it finishes processing the previous message (or at startup). The regular behavior of a peek is to block until a message arrives, but NServiceBus limits this with a timeout so as to allow graceful shutdown at any point of time.". And the line of code I pasted is the mentioned 'peek'.Ashjian
Well, I am not an expert in the field, but looking at the queue.Receive maybe the second parameter is not the most adequate in your case, maybe moving to "MessageQueueTransactionType.None" (if you don't need transaction feature) would be better. The second way to circumvent this (in my opinion) would be to move the timeout feature out of this call, meaning in this call only wait for 0 second, and do the wait somewhere else in your code. I understand theses options may not be ideal as you may have to change the content of the library or your code. good luckHarley
Unfortunately I do not have an answer to this question, however I am experiencing the same behavior. However, I would like to add one piece of information to this. This "slowdown" only occurs when I debug my application from vs. If I run the application without debugging, it operates as it should.Jaipur
Is there any change when you set the timeout period lower or higher (five or twenty seconds). Is this something you can test? Most specifically does the amount of time it takes a queue to fail vary with the length of the timeout you're using?Insensate
Nsevicebus is a mature and stable project, I doubt you need to be poking around under the covers. Its whole point of existence usd to abstract the queue from the developer. Its probably a congiguration issue. Have you tried posting your question to an nservicebus specific forum or group?Quenna
Hi Jason. Yes I have posted the question to the official nServiceBus mailing list. Udi (the guy who created the project) had no insight and nobody else seemed have experienced the issue. I have since moved on with projects and reducing the timeout wait to 2 seconds has made functionality work well enough. We are actually now using RabbitMQ for our main messaging, for various other reasons though. I would be interested to hear if anybody ever suffered the same problem & managed to work out a solution. Cheers.Ashjian
@mrnye, have you tried wrapping the MessageQueue object with a using(resource) {} statement since it is an IDisposable object?Sweeten
S
9

Any particular reason you aren't using an event handler to listen to the queues? The System.Messaging library allows you to attach a handler to a queue instead of, if I understand what you are doing correctly, looping Receive every 10 seconds. Try something like this:

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();
    }
}
Superannuated answered 17/1, 2014 at 19:47 Comment(1)
The main reason is that you cannot receive asynchronously from a transactional queue. This restriction can be traced all the way down to the native API.Mollymollycoddle
F
4

We are also using NServiceBus and had a similar problem inside our network.

Basically, MSMQ is using UDP with two-phase commits. After a message is received, it has to be acknowledged. Until it is acknowledged, it cannot be received on the client side as the receive transaction hasn't been finalized.

This was caused by different things in different times for us:

  • once, this was due to the Distributed Transaction Coordinator unable to communicate between machines as firewall misconfiguration
  • another time, we were using cloned virtual machines without sysprep which made internal MSMQ ids non-unique and made it receive a message to one machine and ack to another. Eventually, MSMQ figures things out but it takes quite a while.
Folia answered 21/2, 2014 at 18:16 Comment(0)
L
0

Try this

public Message Receive( TimeSpan timeout, Cursor cursor )

overloaded function.

To get a cursor for a MessageQueue, call the CreateCursor method for that queue.

A Cursor is used with such methods as Peek(TimeSpan, Cursor, PeekAction) and Receive(TimeSpan, Cursor) when you need to read messages that are not at the front of the queue. This includes reading messages synchronously or asynchronously. Cursors do not need to be used to read only the first message in a queue.

When reading messages within a transaction, Message Queuing does not roll back cursor movement if the transaction is aborted. For example, suppose there is a queue with two messages, A1 and A2. If you remove message A1 while in a transaction, Message Queuing moves the cursor to message A2. However, if the transaction is aborted for any reason, message A1 is inserted back into the queue but the cursor remains pointing at message A2.

To close the cursor, call Close.

Linguistic answered 18/8, 2011 at 9:39 Comment(0)
G
0

If you want to use something completely synchronous and without event you can test this method

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;
}
Gower answered 2/1, 2016 at 18:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.