It is often a requirement to reliably deliver messages even when the network is periodically down or the machine is power cycled. When using Windows, Microsoft Message Queuing (MSMQ) is an old standby for persistent storage of queued messages. This post looks at how to set up MSMQ and interact with it from .NET.
Code Please
A quick start project is available on Github if you would like to go straight to the code.
Installation and GUI Access
MSMQ is an optional Windows component that is not installed by default. Check your Windows Features list (GUI or command line). Check the box for Microsoft Message Queue (MSMQ) Server. Just check the top level which includes Core. We will not be using the other features in this post.
Once installed you can administer queues from Computer Management (compmgmt.msc). Find Message Queuing under the Services and Applications node. We will programatically create our queues in this post but you can manually interact with them here.
System.Messaging
You will need to add a reference (within Assemblies) to System.Messaging
from your project and add a using System.Messaging
statement.
The System.Messaging.MessageQueue class is where the action is. Let’s step through it.
Queue Paths
There are a lot of ways to format the path to a queue. The most common case on an embedded system is a private queue on the local machine, which is addressed with:
.\private$\nameOfQueue
You can also specify a remote machine name. For TCP and HTTP connections:
FormatName:DIRECT=TCP:MyIPAddress\nameOfQueue FormatName:DIRECT=HTTPS://MyServer/MSMQ/nameOfQueue
Note the slashes are in a different direction for TCP vs HTTP. More details here.
Transactions
You need to decide at the time of queue creation whether or not the queue will be transactional. If you are performing an operation that may fail (network call), use a transactional queue. Reading from the queue will not permanently remove the entry until you separately call Commit
. Calling Abort
or disposing the transaction puts the record back in the queue.
Consider the possible failure mode of a message getting stuck in a queue. For example, the network is up but the message is improperly formatted and will never be accepted by the server. There may be valid messages stuck behind it. There are a number of strategies for addressing this situation which are well covered here.
Creating and Opening a Queue
Finally, some code! Here I create the queue if it does not exist or attach to an existing queue. Note I specify this queue is transactional in the constructor:
const string path = @".\private$\test"; MessageQueue queue = null; if (!MessageQueue.Exists(path)) { queue = MessageQueue.Create(path, transactional: true); } else { queue = new MessageQueue(path); }
Serialization
XML and Binary serialization of messages is provided out of the box. If you are able to declare all message types up front, you can set this up once with:
queue.Formatter = new XmlMessageFormatter( new Type[] { typeof(string) } );
I’m just sending strings for example purposes but user defined types are also supported. Strictly speaking, the XmlMessageFormatter is already constructed as a default on the MessageQueue class. You could just set the target types directly but I find this ugly:
((XmlMessageFormatter)queue.Formatter).TargetTypes = new Type[] { typeof(string) };
Another technicality – you can delay specifying a formatter until you are actually sending (see Message constructor) or receiving (see Message.Formatter) a single message. You can dynamically mix message types in the same queue. I wouldn’t suggest that in most cases.
Sending a Message (non-transactionally)
Pretty simple, but with a subtle problem to point out. Note below how I wrap the string in a Message
instance. The Send
method takes an object, so you might think you can skip the wrapper. But don’t. Doing so will make your sending code not thread safe.
string input = Console.ReadLine(); queue.Send(new Message(input));
Reading Messages (non-transactionally)
Another deceptively simple bit of code:
Message message = queue.Receive(); string line = message.Body as string;
So what’s the catch? If there are no messages in the queue, the Receive method will block indefinitely. Typically, a queue reader is implemented on its own thread and is left always running. In that case blocking is just fine. My example project runs another thread for reading.
There is no explicit Count
property on MessageQueue
. The way MSMQ is normally used such a property is ephemeral. If you really need to read a queue until you reach empty, all options are moderately ugly. I do not vouch for any of these:
- Set a timeout on the
Receive
method call, catch theMessageQueueException
and make sureex.MessageQueueErrorCode
isMessageQueueErrorCode.IOTimeout
. Yuk! And only feasible for local queues. - Use GetMessageEnumerator2. The ‘2’ inspires confidence.
- Use MessagePropertyFilter.
Messages with Transactions
Home stretch. To have a separate Commit/Abort phase, create a MessageQueueTransaction
and pass it to Send/Receive. Some gotchas:
- Your queue must be created from birth with the
transactional
attribute set to true. - Be sure to call
Begin
on the transaction before you pass it or it will have no impact. - Even if your program logic does not require a transaction for sending, you will still need to pass the transaction object and commit it. Otherwise you will not get anything in the queue.
MessageQueueTransaction transaction = new MessageQueueTransaction(); transaction.Begin(); try { Message message = queue.Receive(transaction); // Do something that could fail (i.e. network call) transaction.Commit(); } catch (Exception) { transaction.Abort(); }
DevOps
Some options for interacting with MSMQ on a running system:
- MSMQ Management Console (CodePlex, Github)
- PowerShell commands
- Backup MSMQ with mqbkup
Featured Image Credit
Second Childishness and Mere Oblivion. Photo by Nick Bushby.