Using MSMQ in a Distributed Application Download this paper here. Download the code files
associated with this paper.
Introduction Using messaging in a single-tier application is like calling yourself on the phone. What's the point? You are going to be there to answer, assuming you dialed the right number, and you already know what you are thinking, hopefully. So, why bother? Applications have been moving toward n-tier architecture and away from the single, monolithic desktop machine. Imagine you live in one city and your brother lives in another. You pick up the phone to call and say hello. Nobody answers. What do you do? Leave a message on the machine? Call again later? Maybe he left on a vacation and didn't tell you. You might try calling for days and never get through with an urgent message (the prize van and cameras are in front of his house). This is the sort of problem you will run into more and more as your applications become disconnected and spread over unreliable or unknown connections. In the world of internal applications across a network, you have the luxury of knowing your network is up and running. Applications can talk cross process and you know, with a pretty high certainty, that the communication works. However, many applications are now more than internal apps running on a solid network. I'm not just talking about web sites. An application may use the Internet to talk with servers half way around the world to access a business object. As an example, say you own a few small bookstores and you need each store to send daily sales reports to the home office. You could use a dial-up modem to connect and send some data. Or you could even be attached through DSL or cable to the Internet and then on to your office. Now, what if the system fails, the dial-up disconnects, the server crashes or the senders system goes down? You reconnect and try again. You now have the possibility of several full copies or partial copies of your data in the system. Can you be absolutely sure the data was received correctly? These are some of the concerns MSMQ addresses: Guaranteed once-only delivery, transactional messages, prioritization of messages, and connectionless messaging. This paper explores these and other MSMQ topics and shows how you can implement message queuing into your next application. Return to Contents
MSMQ Basics
When setting up an independent client, you will need to know the 'address' of the queue that you wish to send to. To understand how to find out that information, we first need an overview of MSMQ and to look at the basic structures and objects in the MSMQ model. Return to Contents Queues Let's look at how we search for a queue that is up and online out there in MSMQ land. The steps required to get a list of available public queues is not as straightforward as you would hope. First, you need to create an MSMQQuery object to get a MSMQQueueInfos object from which you get a list of all public queues through a MMSMQQueueInfo object.
Dim oQuery As MSMQQuery
Then, by interrogating the oQInfo object, you access any of the information available through the properties dialog of the queue. Perhaps the most import information you need is the name and/or ID for the queue. As we develop our offline application, we need one or the other of these bits of information in order to send to the queue offline.
Figure 2 shows the properties for a queue on my \\southside server. To access this queue offline, we need the ID, its address, and to use it as shown in the following code sample.
Dim oQInfo As New MSMQQueueInfo
You use the queue's ID as part of the FormatName of the queue that you want to open. Now that we know how to find and open a queue, let's see what we need to do to send a message.
Dim oMsg As New MSMQMessage
This most basic of messages shows one simple, but not obvious point. The second parameter of the send method is required if the queue you are sending to was created as a transactional queue. We'll talk more about transactions later, but for now remember that you need to know the transaction state of the queue you are sending to. You can get that information from the IsTransactional property of the MSMQQueueInfo object. A more complete code fragment would look like: Dim oMsg As New MSMQMessage
If you use the MQ_SINGLE_MESSAGE constant and the queue is not a transactional queue, you get an error. It is important to know about your queue beforehand or to query the MSMQQueueInfo object before you send. Making an assumption here could be a big problem for your customers. Return to Contents MessagesIf all you wanted to do was send a simple text message, we could end this section right now. Often, you may want to send a more complex message. Perhaps you have some binary data you need to transfer or you have some application-specific, custom data to send via MSMQ. You can send any data that can be represented as a byte array. You can create a PropertyBag and send it as the message or you can send recordsets through the message body. In fact any object that can be persisted can be sent in an MSMQ message body.
This code sample shows how easily you can create a PropertyBag object on the fly, fill it with data and send it on its way.
Passing a recordset is just as easy--you use a dynamically built recordset or one obtained by querying a database. You pass any object that implements IPersistStorage and assign it to the message body. How do you get the IPersistStorage interface in Microsoft Visual Basic? It is easier than you think. Create a class that has its persistable property set to (1) � Persistable. You then create an instance of that class, fill it with data and assign it to the body of the message.
Dim oBook As New CBook
The preceding sections and code show how to find a queue and send a message to it. But there is much more to MSMQ than sending simple, or complex, messages. Return to Contents
Getting a Reply and Asynchronous Events
In order to send the response queue to the receiver, you will need to create it and get a reference to the object. Let's look at how to create a queue.
Dim oQueueInfo As New MSMQQueueInfo
Notice that you actually create an MSMQQueueInfo object and not an MSMQQueue object. Once you have created an MSMQQueueInfo object, open it to get an MSMQQueue object. You then pass this object in the message.
Set oRespQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
We will look at the receiver end of this communication later, but first let's see how you receive the response back. Remember the idea of our application is to talk to a possibly disconnected client. If we send this message and then sit in a loop waiting for a response, we have sort of shot ourselves in the foot. In order to make this work, we need to use MSMQ events to receive the response asynchronously. First, you connect an event handler with a specific queue.
Private WithEvents qevtRecEvents As MSMQEvent
Once the connection is made, you continue with program execution and the qevtRecEvents_Arrived event occurs when the receiver application responds to your message. As always, there is a pitfall to watch for. Each time the event fires, it must be reset so that it fires the next time.
Sub qevtRecEvents_Arrived(ByVal Queue As Object, ByVal Cursor As Long)
The Queue is passed in to the event and you use that object to receive the returned
message. In the sample above, note the assignment of the Queue parameter to an
MSMQQueue object. This is done to take advantage of IntelliSense. While not
necessary, it is a very useful trick.
Sub qevtRecEvents_Arrived(ByVal Queue As Object, ByVal Cursor As Long)
One additional piece of data we need in the reply message is some method to correlate the reply with the original message. The sending client may use the same queue for replies from multiple clients or it may send several messages to the same client, expecting responses for each message. When MSMQ sends a message it assigns and ID to the message in the form of a GUID. This ID is unique for each message, as all GUIDS are, and can be used by the sending client to correlate it original message with the reply. In the sample code above, you can see where this ID is assigned to the CorrelationID property of the newly created message. Transactions and MSMQ
An internal transaction has a simple limitation, it can only send or receive MSMQ messages. As with the special case of a single message transaction, more complex internal transactions cannot be passed to external resource managers to participate in external transactions. The code to send an internal message transaction includes a reference to the MSMQTransactionDispenser object.
Dim oxDispenser As New MSMQTransactionDispenser
Any messages sent between the BeginTransaction and Commit calls on the MSMQTransactiondispencer are considered to a 'part' of the transaction. This is a very simplified example. In real life the BeginTransaction and Commit method calls may not be in the same procedure scope. When reading these messages from the message queue, you need to verify the transaction boundaries of the messages in the queue. You also need to use three properties of the MSMQMessage object to verify if a message is in a transaction. The IsFirstInTransaction and IsLastInTransaction properties are self-explanatory. If the message is a single-message transaction, then both properties are 'True.' The third property used to determine the boundaries of a transaction is TransactionID. All messages sent from an internal transaction must all come from the same machine. This becomes important when reading the messages from a single transaction in a receiving queue. The messages in the transaction are not sent until the Commit method is called. At this point, they are all sent and will be found in the queue in the order that they were sent. They will also be grouped together. If you have two transactions occurring in your sending application, whichever one is committed first will arrive on the receiving queue first, regardless of when the messages were sent on the sending machine. External transactions are used when a transaction involves more than just sending and receiving messages. They use the Microsoft Distributed Transaction Coordinator as the external resource manager.
Dim xoExtDispenser As New MSMQCoordinatedTransactionDispenser
Notice the transaction dispenser object used is different from the one used for internal transactions. Messages to a receiving queue, which are a part of an external transaction, may come from different sending computers. This can happen when two different machines have to send MSMQ messages as a part of a single transaction. In this case, the assumption we stated earlier about all messages being grouped together in the receiving queue is no longer valid. The messages from the two different machines will not necessarily be grouped together. This makes it a little trickier to retrieve the messages from the queue, as you need to pay close attention to the TransactionID property. Lastly, we need to look at MTS transactions. You may have thought this would be covered as an External Transaction, and that would be partially correct. The COM+ transaction works together with the DTC to handle these type of transactions. In fact, MTS transactions are the default for sending MSMQ messages. The transaction parameter of the MSMQ Message Send method has one of five values.
MQ_NO_TRANSACTION
If you specify no argument for this parameter, MSMQ assumes you are participating in& an MTS transaction. Because you are sending to a queue that was created as a transactional queue, you will need to check COM+ to see if you are running in the context of a COM+ transaction. This gives you the constant to pass to the Send method of the MSMQMessage.
Set oCtxObject = GetObjectContext
While MSMQ itself is rather straightforward, transactions can become quite complex and code heavy. In this paper, we have tried to touch all of the important points you need to be concerned with when writing an MSMQ application. There is an accompanying file with this paper containing all of the code samples in Visual Basic 6.0 project files. Using the sample code and the information in this paper, you should be able to get a quick start on the development of your first, or next, MSMQ application. Return to Contents Get the
Scoop on the Many Useful Features of Windows 2000!
|
NuMega Home ·
Products ·
Shop NuMega ·
Resources ·
Support · Register ·
More Info.
|