Tips for a successful MSMQ-enabled application
For the past few weeks I've been deep down inside a client's project. It's a large distributed application and one of the features I was working on made me realize just how little I've gotten the chance to use MSMQ in my projects, which is a total shame, really.
MSMQ (Microsoft Message Queue) is a great bridge for helping you distribute messages between applications in an asynchronous manner (kinda like email). The syntax for using MSMQ is pretty easy (a couple of lines to send a message to a queue in the simplest form) and the benefits are huge if you need asynchronous conversations between your disconnected application components.
However.
MSMQ, like many other products and components, has its dark corners, as I've found during the past couple of weeks. I'll list them here so that I (and you) don't lose track of them. Some of them I've gotten straight off
Yoel Arnon - MSMQ Guru and once part of the original MSMQ Team at Microsoft.
You can read his blog over here(guess what its about..).
- The problems with remote queues and COM+ transactions
I had quite a simple scenario laid out for me. Get some info from a remote queue and try to insert it into a database. If the database insert fails, rollback the "get" from the queue as well so that the info stays queued for the next time around to be inserted into the DB again. Easy enough I tried to open a new transaction context using COM+ before calling "receive" from the queue and abort it if the DB failed. My integration tests (driven by NUnit) showed that even though Rollback was called, the message did not return into the queue. Weird because this was a transactional Queue and that should have worked. After asking Yoel it turns out that the fact that this queue was
remote(residing on a different computer) meant that you couldn't actually do that. Remote queues do not support a transactional receive inside a transaction. If the queue had been local (on the local machine) this would have worked. "Send" does work in both ways, but receive is somehow special. There are ways around it,
detailed here, but I've managed to get around it by doing the following:
Create a local, private queue(cannot be used from remote machines) which acts as a transactional queue cache. The receive from the remote queue is actually a two-phase receive: get the info from the remote queue and put it into the local cache queue. Then do all your transactional work using the local queue. That guarantees that you never lose a message if you need to rollback on multiple actions.
2. CanSend and CanRecieve = damn lies
If your remote queue tells you that you can't sent or can't receive using its public boolean properties- don't trust it. It might be wrong. That's just how it is.
3. Make sure you have the right permissions
On windows Server 2003 in order to receive from a remote queue you need to explicitly go to the queue permissions page, click on the "Anonymous Logon" role and add the "Receive" ability to it, or you won't be able to receive from the remote queue
It took me several hours to capture these important insights - so now you don't have to. If you have any more "Watch out for" tips on MSMQ - feel free to post a comment to this post.
Good luck!