What a huge surprise, right?
Over the course of building a Microsoft Message Queue based C# application recently, I’ve picked up some best practices. I also found quite a few MSMQ/C# resources on the web that either didn’t do what I wanted or were too outdated to apply. It’s also possible that they were just wrong from the beginning.
My environment is this: a workgroup-based Windows Server 2003 Standard machine, .NET 3.5, and two C# applications. One sends messages, and one receives and processes messages using asynchronous routines. The test user is a local administrator.
The first thing I tried was to run this scenario on my Vista SP1 development machine. Big mistake. The machine is domain joined, but the first time I installed the MSMQ components I forgot to select the checkbox for AD Integration. At this point, my code for using a private queue wasn’t working correctly yet. Then I realized what had happened, uninstalled the MSMQ components, reinstalled with AD support, and tried again. No luck. In fact, after the reboot, the MSMQ components aren’t present at all! I’ve tried several times; the installation rolls back at some point after I authorize the reboot, and afterward, it’s as though it never happened. You kind of get the impression that MSMQ is more of a server SKU component. Has anyone else seen this?
I abandoned Vista, moved to the WS03 machine, and started making forward progress. I quickly learned that the Message Queuing node in the Computer Management MMC is your best friend. Click Start | right-click My Computer | Manage | Computer Management (Local) | Services and Applications | Message Queuing. If you create a queue like mine below and pump some messages into it, you’ll subsequently see a Private Queues | myqueue | Queue messages sub-node, where you can inspect all of the messages. More on what I learned from that in a second. There’s lots of other stuff you can do in that snap-in node; play with it for a bit if you’re new to the technology.
It’s true that workgroup machines really do only support private queues. The reason is that the information for registering and finding public queues is stored in Active Directory, and of course, Microsoft wants to give you lots of reasons to deploy AD. Actually, the workgroup machine limitation is pretty common knowledge, but I saw lots of posts confusing the syntax of private queues. This worked fine for me:
private static string queuePath = @".\Private$\MyQueue";
Then queuePath can be passed directly to the System.Messaging.MessageQueue constructor, for example. Contrary to what I read elsewhere, the explicit DIRECT syntax is not required.
On a related note, the MessageQueue.Exists method (when passed the above string, for example) does work on private queues. I saw lots of posts claiming that it only works on public queues. Not so.
Next: nearly every example on the web shows the use of the MessageQueue.Send method with simple, intrinsic types. In fact, they pretty much all use “string”. One source of potential confusion about Send is that it takes an argument of type object. So, as evidenced by those examples, it’s legal to pump pretty much anything through there.
But best practice is actually to use the Message type and send that. Your payload should be the body; you can pass it to the Message constructor. The key here is that Message has various attributes that are critical for debugging, including UseDeadLetterQueue, UseTracing, UseJournalQueue, and Label. In fact, the latter – Label – is visible in the “Queue messages” display in the MMC pane that I mentioned above. That alone is a huge help for debugging, because you can identify what’s sitting in the queue just with a quick glance.
There’s more, though. I’m sending custom class objects through the queue, but I was originally trying to do so with without encapsulating them in a Message, and it just wasn’t working (messages were stuck in the queue). I didn’t try all possible permutations of the following to identify the exact cause, but I now recognize all of these things as best-practice.
When I opened some of the undelivered messages in the MMC snap-in and examined the payload, I noticed that the XML formatter was being used by default, and that the type was correct. But the private class data fields were missing. The documentation for the XML formatter indicates that, by default, it should pickup all private and public members of a serializable class, excluding those members explicitly marked non-serializable. But my data fields, all marked private and implicitly serializable, were being excluded. Changing them to public fixed that problem.
Next, although I couldn’t find this stipulation in the official docs, there seems to be a requirement, at least when using the XML formatter (again, that’s the default), to specify the allowed types to be passed in the message body. This is done via code such as the following:
queue.Formatter = new XmlMessageFormatter(targetTypes);
where targetTypes is an array of all of the possible types you want to support in your message body. It’s natural to define a common interface for those message-type classes to implement, but I found that you can’t just specify the interface type to the XmlFormatter constructor, above. Actually, I was getting inconsistent results here; some of the inherited types were working and some weren’t (i.e. couldn’t be delivered). Explicitly listing all of the class types in the targetTypes array fixed that.
Thus, even though the XML formatter is the default, any implementation of reasonable complexity is going to have to set it explicitly, due to the requirement of listing your types. As an aside, for small messages in a scenario such as this, XML is way overkill. Don’t be surprised if your messages are ten times the size they’d be versus using the binary formatter.
One thing I thing I found frustrating is the lack of solid, asynchronous MSMQ programming examples in the Microsoft documentation. From the perspective of the designers of the CLR, what are the implementation patterns that are expected to yield the best throughput?
My approach is a simple one. I expect the most load to be on my message receiving side. On the main thread of the receiving application, I perform the following operations:
- Open or create the queue
- Create a stop event
- Add a ReceiveCompleted event handler
- Initiate the first BeginReceive
- Wait for the stop event to be signalled
In the ReceiveCompleted event handler, I first grab the caller context, since that’s where I store the queue reference, among other data. Then call EndReceive on the queue to get the message. Then, before handling the message, immediately call BeginReceive again. That’s a critical step, because it allows other threads to continue to take incoming messages in parallel. If a Stop message is received from the sender, set the stop event and the main thread ends.
One final note: various resources on the web indicate that other settings changes must be made in order to get basic MSMQ scenarios working. For example, DTC and DCOM settings. For this scenario, that’s not required.