MSMQ Part 1/2: Architecture and Simple Implementation Using VB
MSMQ is a messaging solution that uses the store-forward style of distributed computing to share data across networks in terms of messages. In part one of this two part series, Liviu explains the architecture of an enterprise system in relation to MSMQ. He also shows us how to implement a simple message queuing application using Visual Basic 6 and the MSMQ COM objects.
MSMQ Part 1/2: Architecture and Simple Implementation Using VB - Hello world with MSMQ (Page 3 of 5 )
Let's start exploring the capabilities of MSMQ by implementing a typical "Hello World" example. Our example will contain two components: a sender and a receiver. For the purpose of this example, the sender and the receiver will be two separate Visual Basic applications, however in the real world they could easily be the same application, COM/DCOM objects, ASP pages, etc. Our example scenario is outlined as follows:
The sender will try to locate a queue called "Greeting". If the queue can't be located (i.e. the very first time the sender program is started) then it will attempt to create it.
After locating/creating the queue, the sender then posts a message with the text "Hello World!" to this queue. This is the end of the sender's job.
The receiver will try to open the "Greeting" queue. If the queue is not found, however, it wont be created, but instead the receiver will assume that the sender is not yet running and inform the user to try again at a later time, after the "Greeting" queue has been created.
Once the queue has been located, the receiver will read the first message found in the queue and display it to the user. After the message has been retrieved from the queue, it will be deleted.
At this stage we don't process the messages in any useful way because they don’t contain any business logic information that can be used by us. All we're interested in at this stage is sending and retrieving a simple message.
To manipulate queues and messages, our applications make use of the COM objects provided by MSMQ. The MSMQ COM object exposes three objects that are of interest to us: MSMQQueueInfo, MSMQQueue and MSMQMessage. The diagram shown below displays the relationship between these objects:
As you can see, an MSMQQueueInfo object contains a collection of MSMQQueue objects, which in turn holds information about a collection of MSMQMessage objects.
Creating and locating the queue
The first thing the sender does is try to locate a queue called "Greeting". This is achieved using the MSMQQueueInfo object. The only parameter we need to pass to the MSMQQueueInfo object is the path name, which uniquely identifies the queue. The path name takes the form of server\queue_name. Note that we have specified "." for the server name, which denotes the local machine. In other words, we have created a local queue.
Once the path name is set, we can call the Open method on the MSMQQueueInfo object to open the queue. If this fails (the queue couldn't be located) then we can create the queue.
The code in our example applications doesn't necessarily cater for all of the possible errors that can occur because it's just a simple example.
We will assume that the queue doesn't exist if the open method fails, and therefore we need to create it. However, bear in mind that the queue might actually exist but it might not be available to us because of security restrictions or it may have been created with private access.
The open function takes two parameters: an access specifier and a share specifier. The access specifier tells MSMQ what are we planning to do with the MSMQQueue object: will we be send messages, receiving them, or both? The share specifier tells MSMQ how to act when another request to the same queue arrives from another application. This is similar to the way that you specify access to a file when you open it (i.e. allow shared access, allow read only access, etc.).
Obviously we only need send access from our sender application, and because our application is rather simple we don't worry at this stage about shared access to the queue, so we allow shared access of any kind (read/write) from any other application with the MQ_DENY_NONE parameter.
To create the queue we use the same MSMQQueueInfo object, setting its path name. Instead of calling its open method this time however, we call create instead: we are creating a public queue located on the local machine. A public queue is registered with the PEC and is known about by the other machines in the enterprise.
We could've however decided to create a private queue by using a path name like PRIVATE$\queue_name instead. This type of queue wont be registered with the PEC, and therefore wont be known by any of the other machines in the enterprise. Why you would decide to use a public or a private queue is beyond the scope of this article, so consult the MSMQ section at MSDN for more information.
For now however, just be aware that our queue will be a public one and that it can be accessed from any other application running on either the same or a different machine. Sometimes in an application we only need to make sure that we get a handle to a queue –- either by creating it manually or by opening an existing one. Here's a quick (and not so clean) way of achieving that:
Dim qi As New MSMQQueueInfo
Dim q As MSMQQueue
On Error Resume Next
qi.PathName = ".\Greeting"
Set q = qi.Open( <access_specifier>, <share_specifier> )
Sending the message
Once we've located or created our queue, we can start sending messages to it. Sending a message is very simple: firstly we create an MSMQMessage object, setting its label and body properties. Secondly, we invoke the send method on this MSMQMessage object, passing our MSMQQueue object as the only parameter. Think of an MSMQMessage object as an email message that has two basic properties: a subject line (label) and a body. Of course an email message has from and to fields etc, but these fields are automatically completed for us by the send function so we don't have to worry about them. If you have ever worked with MAPI, then you should find it very easy to use this approach when working with MSMQ.
Retrieving the message
As in the case of sending the message, to receive a message we need to use the same three MSMQ objects as before: we use the MSMQQueueInfo object to open our MSMQQueue, and then we use the MSMQQueue object to retrieve the next available message.
Let's look at the details of the parameters for the receive method:
Transaction: For advanced users, transaction is only used when you want to implement a transactional system based on MSMQ, which is beyond the scope of this tutorial.
WantDestinationQueue: If you set this to true, then the queue will be updated when the message is read from the queue. Other applications won’t receive this message, but they will receive the next message that has been posted to the queue. The default value for WantDestinationQueue is false, which means that the next application calling MSMQ’s receive method will receive this message. When WantDestinationQueue is set to true however, you may experience a slow-down in your application, because some network activity takes place between your application and the server to update the queue’s status.
WantBody: When set to true, the whole message will be retrieved. When set to false, only the messages label will be retrieved. This is useful for applications that are waiting for a certain message (as a result to a request posted in this queue for another application), and there is no need to retrieve all of the messages posted in the queue, but only the one that we are waiting for. In such a case, we retrieve just the labels of the messages, and if it's the one that we're after then we will download the entire message and process it.
Timeout: This is one of the most important parameters for the receive method, and it defines how long the application will wait for a message before it times out. Because the call to the receive method is synchronous, this means that your application will be "locked" for the number of milliseconds that you specify as the timeout value. The default value is INFINITE, which means that the receive method will wait until a message is posted in the queue by another application (which we have access to and can retrieve) before it returns. In our example we have set the timeout to 1000 milliseconds (1 second) so we don't lock our application forever.
One the message is retrieved, we can inspect its label and body properties and process them as needed in our application (in our case we will simply set the captions of two labels on our form).
And there you have it, we've just seen the simplest way to "queue" up a client/server application. As you can see, I've used the term client/server, because you can look upon the sender as a client (which sends requests to the server), and the receiver as a server (which processes requests from the client). Of course, client/server architecture involves more that this, but this simple example is, at the end of the day, still a very simple client/server architecture never the less.