MSMQ for .NET Developers, Part 2

art 1 of this article included an example that demonstrated how to read messages from a queue asynchronously. That example worked by first calling BeginReceive on the queue object and then setting a delegate called whenever a new message is received. The BeginReceive method call included a TimeSpan parameter that limited the time to wait for a new message to arrive in the queue to 15 seconds. Using this technique, if a message fails to arrive in the queue within the time allotted, the code calls the ReceiveCompleted delegate?in this case, raising an exception when the EndReceive method is called, signaling the application that no message was received before the timeout expired.

Here is the code used in the original example.

   ' connect to the queue   myReceiveQueue = New _      MessageQueue(".private$DevXTestQueue")   myReceiveQueue.Formatter = _      New XmlMessageFormatter(New Type() _      {GetType(System.String)})      ' add a handler to tell MSMQ which method to call when a    ' message arrives   AddHandler myReceiveQueue.ReceiveCompleted, _      AddressOf MSMQ_ReceiveCompleted      ' call the asynchronous version of Receive,    ' waiting no longer than 15 seconds   myReceiveQueue.BeginReceive(New TimeSpan(0, 0, 15))

In response to Part 1, I received many requests for example code implementing a server application that would “listen” to a queue for incoming request messages. Such an application would receive a message, process the request, and begin listening for the next message immediately. The applications of such a listener are numerous. The example provided in part 1 of the article would not be practical, despite several messages suggesting otherwise. The original example has problems when you extend it to raise exceptions. In addition, as written, the example code doesn’t stop “listening” to the queue when you pause or stop the application. Here’s how you can solve those problems.

How to Stop “Listening” for Messages
All .NET developers, by now, should realize that you should rarely use exceptions to communicate common or frequently occurring error conditions within an application. Exceptions are far too costly to create. For example, when the sample calls BeginReceive with a TimeSpan parameter, thereby setting a timeout, the application will raise an exception when the timeout condition is reached before a message arrives in the queue?in other words, possibly frequently. Perhaps a better way to call BeginReceive is with no parameters, instructing the BeginReceive process to listen indefinitely. Doing that helps by limiting the number of exceptions raised, but also introduces a new dilemma: How do you stop the BeginReceive process?

To cancel the BeginReceive process you should need to call only the queue’s Close method. However, just as I tried many times, many developers found that calling Close doesn’t cancel the BeginReceive call. The ReceiveCompleted delegate would still be called when new messages arrived in the queue. The workaround is to disable the connection-caching property used by MSMQ. Unchecked caching of MSMQ’s queue connections cause the queue objects to remain in memory unexpectedly, causing the condition described above. To prevent this problem, simply set the EnableConnectionCache property of the receive queue to False when creating the queue object. That change cancels the BeginReceive call as expected when you call the queue’s Close method.

The only other requirement to make a “listener” server as described above is to re-enable listening each time a message is received. After receiving each message, you must call BeginReceive again to instruct MSMQ to start listening for the next message to arrive. You do not need to set the formatter or the ReceiveCompleted delegate again.

The code sample below shows the completed Listening Server solution. The following changes have been made to the original solution (these changes are included in the sample code).

  1. After creating the MessageQueue object, its EnableConnectionCache property is set to False.
  2. The code now calls BeginReceive without specifying a TimeSpan parameter.
  3.       ' connect to the queue   myReceiveQueue = New MessageQueue(comboQueueName.Text)      ' IMPORTANT: disable the MSMQ connection cache   myReceiveQueue.EnableConnectionCache = False      ' set the formatter for the queue   myReceiveQueue.Formatter = New XmlMessageFormatter( _      New Type() {GetType(System.String)})      ' add a handler to tell MSMQ which method to call    ' when a message arrives   AddHandler myReceiveQueue.ReceiveCompleted, _      AddressOf MSMQ_ReceiveCompleted      ' call the asynchronous version of Receive - no time limit   myReceiveQueue.BeginReceive()   

  4. In the ReceiveCompleted method, re-enable listening by calling BeginReceive.
  5.       ' receive the message   myMessage = myReceiveQueue.EndReceive( _      p_objAsyncResult.AsyncResult)      ' process the message   Me.ProcessMessage(myMessage.Body)      ' re-enable 'listening' by calling BeginReceive again   myReceiveQueue.BeginReceive()   

  6. Cancel the BeginReceive call by calling the queue object’s Close method.
      ' close the queue (as long as the EnableConnectionCache    ' property of the queue is set to false), this will cancel    ' the pending BeginReceive call   myReceiveQueue.Close()

Sending Typed Messages
In Part 1 of this article, I demonstrated how to send and receive simple text messages. But MSMQ actually provides an easy to use mechanism to send objects from one application to another using queuing. Conceptually, it seems nearly effortless for MSMQ to send and receive strings. The truth is, MSMQ treats strings and custom types very similarly; there’s little distinction between handling a string and handling a more complex type. MSMQ simply serializes the object into the body and sends the message. When the message is received, MSMQ de-serializes the object back into its native type (using a little coercion, of course). The only differences between sending a string and a custom type are that you need to specify a formatter to use and coerce the object back into the intended type after it’s received.

To illustrate using custom types in MSMQ, the code below shows a very simple object used to store article publication information: article name, URL, and publication date. The property implementations have been removed for brevity, because they simply read and write the private member variables.

   Public Class DevXTestClass         Private m_strArticleName As String      Private m_strArticleURL As String      Private m_dtePublishDate As DateTime         Public Property ArticleName() As String         ...      End Property         Public Property ArticleURL() As String         ...      End Property         Public Property AriclePublishDate() As DateTime         ...      End Property         Public Overrides Function ToString() As String         ...      End Function      End Class

To use the class, you create and populate a DevXTestClass instance and send it to a queue.

   Dim myQueue As MessageQueue   Dim objDevX As DevXTestClass      ' create and populate a DevXTestClass object   objDevX = New DevXTestClass   objDevX.ArticleName = "MSMQ for .NET Developers"   objDevX.ArticleURL = _       "http://www.devx.com/dotnet/Article/27560/0"   objDevX.AriclePublishDate = DateTime.Parse("3/16/2005")      ' connect to the queue and send the message   myQueue = New MessageQueue(comboQueueName.Text)   myQueue.Formatter = New XmlMessageFormatter( _      New Type() {GetType(DevXTestClass)})   myQueue.Send(objDevX)

Finally, the code below retrieves a message containing a DevXTestClass object from the queue and exposes the object’s properties (via the DevXTestClass’s ToString method) to show they have not changed in transit through the queuing system.

   Dim myQueue As MessageQueue   Dim myMsg As System.Messaging.Message   Dim objDevX As DevXTestClass      ' connect to the queue and set the message formatter   myQueue = New MessageQueue(comboQueueName.Text)   myQueue.Formatter = New XmlMessageFormatter( _      New Type() {GetType(DevXTestClass)})      ' receive the message, waiting no longer than 5 seconds   myMsg = myQueue.Receive(New TimeSpan(0, 0, 5))      ' convert the message to a DevXTestClass type   objDevX = CType(myMsg.Body, DevXTestClass)      ' display the received message   MessageBox.Show(objDevX.ToString, "Typed Message Received")

As you can see, sending objects is only slightly more complex than sending string messages.

Internet Messaging
One of the more highly acclaimed features in MSMQ version 3.0 is the ability to send messages over the Internet?a feature known as Internet Messaging. The nature of the message does not change, except that MSMQ formats the message as a SOAP message. The feature is useful because it allows sending applications to route messages through firewalls over port 80. Further, by using SSL you automatically gain the advantages of the high-grade security built into the transport, alleviating the need for a VPN to ensure payload security.

To send a message using Internet Messaging, the server must have MSMQ HTTP Support installed using the Windows Component Wizard. Figure 1 shows the Windows Component Wizard with the Message Queuing details window open.

?
Figure 1: Installing MSMQ HTTP Support: You must install MSMQ HTTP support to be able to send and receive MSMQ messages via the Internet.

When installed, MSMQ adds a new Virtual Directory in IIS named MSMQ. MSMQ uses this directory to install a new handler to receive MSMQ SOAP messages?it receives messages and forwards them to the local MSMQ service. Assuming the appropriate firewalls have been configured to allow SOAP traffic on port 80 to your IIS server, there is only one more step, changing the format name.

To use Internet Messaging, simply change the format name, instructing MSMQ to use HTTP (or HTTPS). For example, to send a message to a private queue named DevXTestQueue on a server named MyServer, use the following format name: DIRECT=HTTP://MyServer/MSMQprivate$DevXTestQueue.

One caveat: you cannot listen on remote queues using Internet Messaging. Listening still uses port 1801, and you will get an MSMQ error if you attempt to open a queue for reading using an Internet Messaging format name. You should also be warned that remote reads are rarely a good idea, regardless of whether you attempt to use Internet Messaging or not.

More Useful Message Properties
In my previous article, I stated that MSMQ provides a prioritized first-in-first-out (FIFO) queuing system. It was probably clear that messages are processed in a FIFO manner, but that doesn’t explain the issue of how MSMQ deals with prioritization. MSMQ provides a facility to modify the priority of messages, causing messages with a higher priority to be placed in the receive queue ahead of other lower priority messages (even if the lower priority messages arrived first).

The Priority property is of type MessagePriority, which is a .NET enumeration that provides eight strongly typed values that you can assign to a message. Message priority can range from 0, the lowest priority, to 7, the highest priority. The default message priority is 3. The equivalent MessagePriority enumeration named values range from Lowest and VeryLow to VeryHigh and Highest?the default is Normal. You set the message’s priority before sending it. The code sample below demonstrates how to set the priority on a message object to High.

   myMessage = New System.Messaging.Message()   myMessage.Priority = MessagePriority.High

The Recoverable Property
In its default mode, MSMQ delivers messages in a mode intended to deliver high-performance at the cost of an increased risk that some messages could get lost if the machine hosting the message queues were to lose power (or if the MSMQ service were restarted). The messages are stored in a memory structure, without explicitly being written to disk. If your application architecture requires a higher level of certainty that a message will be delivered, you can set the Recoverable property for messages to True (the default is False). As described in a previous article, Nine Tips to Enterprise-proof MSMQ, you will incur a performance penalty for making messages recoverable. The code below demonstrates setting the Recoverable property for a message.

   myMessage = New System.Messaging.Message()   myMessage.Recoverable = True

The UseDeadLetterQueue Property
The dead letter queue is a special-purpose queue designed to store messages that could not be delivered. The idea is that your application could periodically poll the dead letter queue for errant messages and handle them appropriately. By default, MSMQ deletes messages that cannot be delivered, but by setting a message’s UseDeadLetterQueue property to True (the default is False), any messages that reach the end of either TimeToReachQueue or TimeToBeReceived will be moved to the dead letter queue. Unless it is important?and even then only if you have an automated task patrolling the dead letter queue?I would discourage you from using the dead letter queue. Unchecked message volumes can grow and cause problems with MSMQ on the machine. If you must use it, the code sample below demonstrates setting the UseDeadLetterQueue property on a message.

   myMessage = New System.Messaging.Message()   myMessage.UseDeadLetterQueue = True

The UseJournalQueue Property
Journaling is a feature of MSMQ that creates a debug trace of all messages sent or received. When you set the UseJournalQueue property for a message, MSMQ places a copy of the message in the journal queue (in the System Queues folder of Computer Management) when it sends the message. To journal messages received from a queue, set the queue’s UseJournalQueue property to True. Note that to journal messages as they are sent, you set the property on the message object, but to journal messages as they are received from a queue, you set the property on the queue object. Exercise caution when using journaling, because (like the dead letter queue) it can quickly and stealthily add tremendous volumes of messages to queues on your MSMQ server. The code sample below demonstrates setting the UseJournalQueue property on a message.

   myMessage = New System.Messaging.Message()   myMessage.UseJournalQueue = True

The DefaultPropertiesToSend Class
Often, messages sent to a queue will have the same properties set (such as Priority, Recoverable, etc.). MSMQ provides a facility for automatically setting the properties on all messages sent using the same queue object, so you can easily send messages with the same properties and simplify messaging development by avoiding manual message creation.

Here’s how to use the DefaultPropertiesToSend class. Create an instance of the DefaultPropertiesToSend class and set all the properties that you would normally set on each message, and then set the DefaultPropertiesToSend property on the queue object to the DefaultPropertiesToSend object that you just populated. Despite the fact that the names of the class and queue property are the same, the use of the object should be fairly self explanatory. The code sample below demonstrates setting each of the properties covered above using the DefaultPropertiesToSend property.

   Dim objDefProps As New System.Messaging.DefaultPropertiesToSend      objDefProps.Priority = MessagePriority.High   objDefProps.Recoverable = True   objDefProps.UseDeadLetterQueue = True   objDefProps.UseJournalQueue = True      myReceiveQueue.DefaultPropertiesToSend = objDefProps   myReceiveQueue.Send("Test Message")

MSMQ and its plethora of features has been the subject of entire books. Rather than try to cover all its features, this two-part article demonstrated several of the most widely useful features supported by MSMQ. I encourage you to do more MSMQ exploration and experiment with its many features before building an application framework around it.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Related Posts