Home > Articles > Programming

Designing Message Queuing Applications

  • Print
  • + Share This
Advanced MSMQ Programming focuses on the internal programming procedures and concepts associated with Microsoft's Message Queuing Server. Experienced NT/Windows developers and C programmers receive an in depth look at the MSMQ architecture, programming techniques, securing techniques, and configuring techniques. Learn how to solve interoperability and programming issues associated with the MSMQ and IBM's message queuing middleware, MQSeries and the Microsoft Transaction Server (MTS).

Chapter 1 Designing Message Queuing Applications

Message queuing products provide powerful mechanisms for connecting applications together. As we saw in the Introduction, there are many benefits to using message queuing as the communications layer in an application. The power and flexibility of the products means that almost any application that can be envisaged can be built. However, this very flexibility also means that it can be difficult to decide exactly how to design and implement a particular application.

As a very simple illustration of this problem, when we send a message, IBM's MQSeries product defines over a dozen different options, many of which can be used in combination with one another. Microsoft's MSMQ provides similar features and similar set of challenges for designers and developers. The issue in writing message queuing applications is in knowing which combinations of product facilities to use for particular application scenarios.

One effective way to reduce the difficulty of designing these applications is to employ Design Patterns. These are standard application scenarios, that are commonly encountered, and for which the message queuing design has already been developed. Design patterns frequently supply the complete solution for an application. If not, they can provide a good starting point for development of a more sophisticated design.

In this chapter you will learn:

  • How design patterns can dramatically simplify the decisions required when designing a message queuing application. If your application fits a well-known pattern, all the design decisions have already been made for you.

  • How to design applications that use message queuing to retrieve information from servers. Client applications can retrieve data synchronously or asynchronously from server applications.
  • How to design applications that use message queuing and update data on servers. Client applications can cause updates synchronously or asynchronously and can optionally request acknowledgement.
  • How to design a message queuing application that involves more than one kind of computer system.
  • Message queuing applications can span dissimilar computer systems that represent data differently. Special thought needs to be given to data transported between dissimilar systems.

  • How to select a message queuing design to meet specific application needs. Different application needs demand different designs for applications that use message queuing.

  • How to choose particular message queuing product features to meet the needs of specific applications. Translating a specific design into an implementation requires selection of the appropriate features of the chosen message queuing product.

Design Patterns for Message Queuing Applications

Design patterns have been used for years to define good ways of exploiting particular software technology in applications. A design pattern is a definition of a standard way of using a technology in a way that has been found generally useful. Although design patterns came to prominence to help designers using object-oriented languages, the approach is generally useful.

Most message queuing applications tend to follow one of a small number of patterns. The patterns allow quick decisions to be made about which product features to use. Without them, the choice can be overwhelming. The patterns also define how specific messaging errors should be handled.

In the following sections, we will look at five common patterns to see their characteristics and to see which product features can be used to implement them. These patterns can be used to construct complete applications, or major functional components of larger applications. It is also possible to use several patterns in the same application, each providing one major function.

Pseudo Synchronous Inquiry-Style Applications

Figure 1.1 shows a pseudo synchronous inquiry-style application. In an inquiry application, a client program requests information from a server. Often, the client cannot continue until the information is available. An example of this kind of application occurs in telephone-based, customer support centers. When a customer calls the center, they speak to an assistant who deals with their request. The first activity of the assistant is to retrieve the customer's details. Until these are available, the call cannot progress. From the assistant's point of view, retrieval of the customer information is a synchronous activity, regardless of how it is implemented. Even if a completely asynchronous communications mechanism, such as e-mail, were to be used, the assistant would still have to wait for a response before being able to continue the call. On the client, we can capture this pseudo synchronous behavior in a single subroutine, marked as Make Request, in Figure 1.1. This routine sends a request to the server and waits for its response. On the server, processing the request needs to occur after reading it and before replying. We can use separate routines for reading the request and sending the reply. Processing involves reading the data requested by the client.

Fig. 1.1 Message flows in a pseudo synchronous inquiry-style application.

Major Features of the Pattern

The pseudo synchronous inquiry pattern has two main characteristics. First, of course, it is effectively synchronous at the client. A client-side implementation, for example as a subroutine, will not return control until either the result is available or the operation has failed. The implementation encapsulates the multiple message queuing operations needed by the pattern.

Second, the pattern is an inquiry. Data at the server is not affected by running the pattern. The operation can be repeated as many times as is necessary, should it fail. In this respect, the inquiry pattern is very similar to browsing a web page. We readily accept that web pages are sometimes temporarily unavailable. When we fail to access a page, we routinely repeat the attempt. The same is true for this design pattern. If an inquiry attempt fails, we expect the user to repeat the operation. Because inquiries can be repeated, the error handling associated with failure of the operation is relatively simple. There is no need to employ complex recovery logic, during inquiries. We expect users to repeat them if they fail.

As we will see later, not all operations are repeatable in this way. Generally, any operation that changes data on the server will yield different results if repeated. A good example is an operation that debits $100 from a bank account.

Implementing the Pattern

In the following sections, we'll look at the features of MQSeries and MSMQ that can be used to implement this style of application. In Chapter 2 we'll see a practical example of an MQSeries implementation, and in Chapter 3 an MSMQ implementation.

Message Attributes

One of the first decisions to be made when designing an application is the type of messages that will flow between the participants. We already know that for inquiries, we do not need recoverability. So we can choose message types that do not provide this capability. For MQSeries, this means that we can use non-persistent messages, both for the inquiry and for the results. For MSMQ it means that we can use the express message delivery property. In either case, these non-recoverable messages provide much faster communications than their recoverable counterparts. Recoverable messages need to be written to disk, whereas non-recoverable messages can be processed in memory.

Queue Attributes

Another important decision for the design is the type of queues to be used for the inquiries and the results. Again, as we don't need recoverability, this decision is straightforward. For the results flowing to the client, we can use a temporary dynamic queue on MQSeries. This type of queue is created automatically by MQSeries, when first used. It is destroyed automatically when the client ends. On MSMQ, we can create a queue for the client to use and destroy it when the client ends.

For the inquiries flowing to the server, we need a queue that can be located by the clients that need to use it. On MQSeries we can use a normal, predefined queue. On MSMQ we can use a public queue, that has an entry in the MQIS allowing it to be located by name.

Retrieving Results in the Client

The client retrieves the inquiry results by reading the message returned from the server. An important design decision is how long to wait for the result. We could choose to wait indefinitely. However, that will result in the client application hanging if the server is unavailable. It is usually better to wait for a predetermined time. The length of time is dependent on the needs of the application and the load on the network and the server. Values of 10-30 seconds are usually appropriate when the client is an end-user application.

Of course, even if the client abandons waiting for its results, the message is still in the system. The inquiry may well be processed at some future time and a corresponding result returned to the client. If this happens, the client will receive a response to a request, other than the one it is expecting. It is important for the design to identify and handle these delayed replies. Identification can utilize the message correlation features provided by both MQSeries and MSMQ. These allow results to be correlated with inquiries. The client can then simply discard any reply that is not associated with the current request.

A further refinement is to exploit the message expiry facilities in MQSeries and MSMQ to define the lifetime of the message. A message can be marked so that if it remains in the system for too long without being processed, it is automatically discarded. Setting the lifetime of the inquiry message to be about the same as the timeout used when waiting for the results can reduce the number of delayed requests processed unnecessarily.

Sending Results to the Client from the Server

The server needs to return results to the client that requested them. We can use the reply-to-queue supplied in the client's request as the destination for the results. This information is available for both MQSeries and MSMQ. However, the client is using a queue that exists only while it is running. It is quite possible for the queue to have been deleted by the time the server replies. This is particularly true if the inquiry has been delayed substantially by a network fault or because the server was overloaded. However, since we know that this operation is an inquiry, it is safe for the server simply to discard any results that cannot be returned to the client.

Fire and Forget Update-Style Applications

Figure 1.2 shows a fire and forget update application. This kind of application requests changes to data at a server, but does not need to wait for acknowledgement before proceeding. We can use our example of a telephone-based, customer support center once more. Suppose that a customer calls the support center to notify them that her mailing address has changed. The assistant fills in the new details and submits the update. It is not necessary to make the customer wait for acknowledgement that the update has occurred before completing the call. By using the appropriate features of the message queuing system, we can be assured that the update will take place. Figure 1.2 shows a client application sending a request to a server that results in data being updated. There is no reply to acknowledge that the update is successful.

Fig. 1.2 Message flows in a fire and forget update-style application.

By removing the need for immediate acknowledgement, we can reduce the total load on the server. Address updates will typically be a relatively low priority operation. As long as the update occurs before the organization next needs to communicate with the customer by mail, all will be well. It might be minutes or even hours before the request is actually processed. By removing the need for immediate, synchronous acknowledgement, we can release server capacity, to deal with urgent operations, by delaying less critical work.

Major Features of the Pattern

The fire and forget update pattern has two main characteristics. First, it is entirely asynchronous. The client sends the update request and then immediately continues with other processing. The client assumes that the request will be processed at some point in the future. The client does not receive any acknowledgement of its request.

Second, it is an update. Data is going to be modified at the server. It is important that the update is not lost. It may be important not to repeat it inadvertently.

Implementing the Pattern

In the following sections, we'll look at the features of MQSeries and MSMQ that can be used to implement this style of application. In Chapter 4 we'll see a practical examples of implementations.

Message Attributes

In contrast with inquiries, updates require some level of guarantee that messages are delivered. The level of guarantee required is a function with the value of the data or the cost associated with recreating it should it be lost.

With MQSeries we can use a persistent message to hold the update request. Persistent messages are protected from loss even if a server crashes or a network fails. This kind of message is delivered once, and once only, fulfilling the other criterion for this pattern.

With MSMQ we can use the message delivery property to request reliable message delivery. This is not quite identical to persistent messages in MQSeries. Although MSMQ guarantees to deliver messages with this delivery property, it does not guarantee once and once only delivery. It is possible, though unlikely, that the server might be sent the request message more than once.

For many update applications, duplication of a message is not really an issue. Updates that completely replace old data with new data should still give correct results if repeated. A good example is the replacement of a customer's name and address.

However, some kinds of updates will give incorrect results if repeated. A debit operation on a checking account will yield incorrect results and an irate customer if inadvertently repeated. This kind of operation demands once and once only delivery. To get true once and once only delivery with MSMQ we need to use transactional messages. We'll cover the use of transactional messages in Chapter 8.

Queue Attributes

There is only one queue involved in this pattern. Update requests flow to the server on this queue. As with the inquiry operation, we need a queue that can be located by the clients that need to use it. On MQSeries we can use a normal, predefined queue. On MSMQ we can use a public queue, that has an entry in the MQIS and allowing it to be located by clients.

Transactional Integrity

Although transmission of messages between applications can be made highly reliable, through the use of the appropriate product features, it is still possible for application failures to compromise integrity. For example, suppose that a server reads an update request message from a queue, but then suffers a failure that causes it to crash before the update is made. The message was successfully delivered, but even so, it has now been lost, without the update being performed. As we saw in the introduction, the answer to this problem is to perform the messaging operation and the data update within the same unit of work. If the server fails, data updates are backed out, and the message is restored to the input queue ready to be reprocessed, once the error causing the failure has been resolved.

We can use transactions with just about any MQSeries queue. The use of transactions is specified at the message level. In contrast, transactional integrity is a property of an MSMQ queue and must be specified when the queue is created. In practice, this difference in approach is of minor importance. We'll cover transactional messaging in Chapter 8.

Asynchronous Inquiry-Style Applications

Figure 1.3 shows an asynchronous inquiry-style application. It looks similar to the pseudo synchronous inquiry-style application of Figure 1.1. From the message queuing standpoint, many of the design decisions yield the same results as for the pseudo synchronous case. However, there is one major difference. The client application does not wait for replies to the requests it has made, before continuing to process. For this pattern to be applicable there must be something useful that the client can do even though it has not received a reply. It could be, for example, that the client is continually sending requests and then processing the replies as they become available.

Since the client application does not wait for replies to its requests, some other mechanism needs to be employed to receive them when they arrive. One alternative is to design a multithreaded client, in which one thread is dedicated to waiting for results to arrive. Another alternative is to use an event-based API if it is available. We'll look at both these possibilities in sections that follow.

Fig. 1.3 Message flows in an asynchronous inquiry-style application.

Major Features of the Pattern

The asynchronous inquiry pattern has two main characteristics. First, of course, it is asynchronous at the client. Once the request has been issued, the client continues with other processing without waiting for a reply.

Second, the pattern is an inquiry. Data at the server is not affected by running the pattern. The operation can be repeated as many times as is necessary, should it fail.

The major difference between this inquiry style and the pseudo synchronous style is that we don't regard delayed replies as errors.

Implementing the Pattern

In the following sections, we'll look at the features of MQSeries and MSMQ that can be used to implement this style of application. In Chapter 6 we'll see practical examples of implementations.

Message Attributes

As with the pseudo synchronous inquiry, we can use non-recoverable messages. This means that, with MQSeries, we can use non-persistent messages, and with MSMQ we can use the express message delivery property.

Queue Attributes

Again, the situation is very similar to that for the pseudo synchronous case. For results flowing to the client, we can use a temporary dynamic queue on MQSeries. On MSMQ, we can create a queue for the client to use and destroy it on termination.

For requests flowing to the server, we need a queue that can be located by the clients that need to use it. On MQSeries we can use a normal, predefined queue. On MSMQ we can use a public queue, which has an entry in the MQIS.

Retrieving Results in the Client

The client application, in an asynchronous inquiry, does not wait for receipt of the results. Some other mechanism must be used to receive the result message and make the information made available to the client.

There are two basic approaches. One involves starting a second thread of execution within the application. This thread waits for the arrival of result messages. When a message arrives, the thread reads it and stores the data it contains in the appropriate application data structures.

The second approach requires that the chosen message queuing product's API can support asynchronous message receipt. MSMQ has this capability. MQSeries also has it, but only on mainframe systems.

MSMQ can call an application subroutine whenever a message arrives. Alternatively, it can generate a Windows event. MQSeries can generate an MVS event in response to message arrival. On MQSeries implementations other than MVS, the multithreading option needs to be used to implement this pattern.

Sending Results to the Client from the Server

As with the pseudo synchronous inquiry, we can use the reply-to-queue supplied in the client's request as the destination for the results. Again, the client is using a queue that exists only while it is running. It is quite possible for the queue to have been deleted by the time the server tries to reply. Since we know that this operation is an inquiry, it is safe for the server simply to discard any results that cannot be returned to the client.

Pseudo Synchronous Update-Style Applications

This is the most tightly coupled pattern that we'll deal with in this chapter. In many respects it is the least satisfactory of all the patterns we will look at. Yet it is actually the pattern adopted by most client/server applications that do not use message queuing, simply because alternative mechanisms of application distribution offer little else. For example, any remote procedure call or distributed object method invocation effectively uses this pattern internally, though not over a messaging transport obviously. We'll wee why this pattern is unsatisfactory shortly.

Figure 1.4 shows the message flows. As with the synchronous inquiry pattern, the client cannot continue until it has received the reply from the server. As a result, we can encapsulate the messaging operations into a single routine that sends the request and waits for the reply. At the server we need separate routines to read the request and to send the acknowledgement and code to process the request.

Fig. 1.4 Message flows in a pseudo synchronous update.

Major Features of the Pattern

This pattern has two main characteristics. First, it is synchronous. The client sends the update request but must wait for the acknowledgement before continuing. The client requires some kind of positive acknowledgement of the success of the processing at the server before proceeding. Second, it is an update. Data is going to be modified at the server. It is important that the update is not lost. As we've seen before, it may be important not to repeat it inadvertently.

The problem with this pattern is what to do at the client if an acknowledgement is not received from the server. In the case of the inquiry operation, we could simply stop waiting and ignore any reply that arrived subsequently. We can't do this when an update is being acknowledged. Usually the client is waiting for confirmation that the operation executed on the server before updating client data. If the acknowledgement is delayed, the client has no idea whether or not the server executed the operation correctly. Since the operation is synchronous, it does not allow for the possibility that the server will reply later. The client probably has to assume that the operation failed, remember the failure, and attempt to resolve the issue by communicating later with the server. All of this additional logic needs to be added to the application. There is no additional help available from the infrastructure in solving this kind of problem, regardless of whether the underlying technology is message queuing, remote procedure call or distributed objects. We saw, in the Introduction, that infrastructure solutions to this kind of problem require distributed transaction processing of the kind provided by products such as BEA Tuxedo, IBM CICS and Microsoft Transaction Server. Such systems guarantee that updates occurring on the client and the server are all within the same unit of work and either occur together or not at all. However, a simple change of pattern, using the asynchronous update with acknowledgement instead, can yield virtually the same result with a much simpler infrastructure. Distributed transaction processing systems, especially those that span different kinds of machines, tend to be significantly more expensive to buy and more difficult to operate than message queuing systems.

Implementing the Pattern

In the following sections, we'll look at the features of MQSeries and MSMQ that can be used to implement this style of application. We'll be forced to accept restrictions in the way we implement this pattern because it is synchronous. We recommend avoiding this pattern wherever possible. If you need to build applications that need update with acknowledgement, either use the asynchronous version of this pattern, described in the next section, or consider using distributed transaction processing. Applications that superficially seem to require synchronous update with acknowledgement often turn out not to need this. Designers frequently mistakenly make this assumption. It's worth examining the application requirements very carefully to see if this pattern can be avoided. The consequences of using it unnecessarily can be severe and expensive.

Message Attributes

With MQSeries we can use a persistent message to get once and once only delivery of the request and the acknowledgement. With MSMQ we can use the message delivery property to request reliable message delivery for both update and acknowledgement. As we have already seem, this is not quite identical to persistent messages in MQSeries. Although MSMQ guarantees to deliver messages with this delivery property, it does not guarantee once and once only delivery. It is possible, though unlikely, that the server might be sent the request message more than once.

We've already seen that for many update applications, duplication of a message is not really an issue. Updates that completely replace old data with new data should still give correct results if repeated. Also, receiving duplicate acknowledgements for a particular request should not cause problems at the client. However, some kinds of updates will give incorrect results if repeated and for these we need the once and once only delivery associated with MSMQ transactional queues.

Queue Attributes

Update requests flow to the server on its request queue. As with the inquiry operation, we need a queue that can be located by the clients that need to use it. On MQSeries we can use a normal, predefined queue. On MSMQ we can use a public queue, that has an entry in the MQIS and allowing it to be located by clients.

Once again, as this is a synchronous pattern, acknowledgements that arrive other than when the client is waiting for them specifically are discarded. We can delete the reply queue when the client is not active. However, as we need to use persistent messages on MQSeries, we must use a permanent dynamic queue and control its deletion through the MQSeries API. MSMQ is unaffected.

Transactional Integrity

Although transmission of messages between applications can be made highly reliable, through the use of the appropriate product features, it is still possible for application failures to compromise integrity. For example, suppose that a server reads an update request message from a queue, but then suffers a failure that causes it to crash before the update is made. The message was successfully delivered, but even so, it has now been lost, without the update being performed. As we saw in the introduction, the answer to this problem is to perform the messaging operation and the data update within the same unit of work. If the server fails, data updates are backed out, and the message is restored to the input queue ready to be reprocessed, once the error causing the failure has been resolved.

We can use transactions with just about any MQSeries queue. The use of transactions is specified at the message level. In contrast, transactional integrity is a property of an MSMQ queue and must be specified when the queue is created. In practice, this difference in approach is of minor importance.

Asynchronous Update-Style Applications with Acknowledgement

Figure 1.5 shows an asynchronous update application in which an acknowledgement is required. In this case, the client making the request is interested in the progress of the update, but does not need to wait for it to complete before continuing.

Fig. 1.5 Message flows in an asynchronous update with acknowledgement.

Major Features of the Pattern

This pattern has two main characteristics. First, it is entirely asynchronous. The client sends the update request and can immediately continue with other processing. The client assumes that the request will be processed at some point in the future, but does need some kind of positive acknowledgement when that happens.

Second, it is an update. Data is going to be modified at the server. It is important that the update is not lost. It may be important not to repeat it inadvertently.

Topologically, this pattern is very similar to the asynchronous inquiry that we looked at in the previous section. The major difference is that, as we must be careful not to loose the request or the acknowledgement we must use reliable or persistent messages.

Implementing the Pattern

In the following sections, we'll look at the features of MQSeries and MSMQ that can be used to implement this style of application.

Message Attributes

The issues concerning message attributes are just the same in this pattern as for the pseudo synchronous update with acknowledgement pattern that we looked at in the last section. The updates and acknowledgements require some level of message delivery guarantee. The level of guarantee required is a function with the value of the data or the cost associated with recreating it should it be lost.

With MQSeries we can use a persistent messages to hold the updates and acknowledgements and give once and once only delivery semantics. With MSMQ we can use the message delivery property to request reliable message delivery. This is sufficient as long as the occasional duplication of an update of reply is acceptable. If it is not, we need to use transactional messaging and queues.

Queue Attributes

On MQSeries we can use a normal, predefined queue for requests flowing to the server. On MSMQ we can use a public queue, that has an entry in the MQIS allowing it to be located by clients. A difference from previous examples is the client's reply queue. As the client is asking for acknowledgement, we have to assume that this is important. It would be inappropriate to use a queue that is deleted when the client terminates. We can use either a predefined local queue on MQSeries or a permanent dynamic queue. Permanent dynamic queues are created when first used, but are not automatically deleted. On MSMQ we simply avoid deleting the client queue when the client ends. In both cases we need to be able to identify the queue so that when the client restarts, it can open it and find any replies that arrived while it was not running. Naming conventions based on an end user's identifier are often convenient for this kind of application.

Transactional Integrity

Issues of transactional integrity apply to this pattern just as to other update patterns. If we want to protect ourselves from the applications loosing messages, we need to implement some level of transactional integrity. We'll cover this in detail when we look at transactions and message queuing in Chapter 8.

Designing for Heterogeneous Networks

Distributed applications based on message queuing are frequently deployed across different kinds of computer system. Special attention needs to be paid to particular aspects of the design of an application, when multiple systems are involved. For example, different systems represent data in different ways. IBM's Mainframe and AS/400 systems use EBCIDIC encoding for characters. Almost all other systems use ASCII encoding. Even the way that integers and floating point numbers are represented varies between different systems.

The preferred development environment and programming language varies from system to system. On mainframe systems, COBOL is normally the preferred language. On Windows systems, it is Visual Basic. On UNIX, the preferred language is often C or C++. Java is becoming increasingly common on all platforms.

Differences in data representation and in programming language can significantly complicate the task of building a working application. Different programmers develop the client and the server. They are familiar with the platform on which they are working, but may know little or nothing about the one with which they are trying to communicate. Imagine the difficulty of a COBOL programmer and a Java programmer trying to discuss the layout of the messages that they need to exchange. The COBOL programmer has no idea what a class is and the Java programmer doesn't understand PIC'9'!

Designing Messages for Heterogeneous Networks

Successful implementations of applications that span multiple platforms are most likely when the design of the messages to be exchanged follows some very simple rules.

The first rule is to use only character data in messages. Character data is handled almost identically regardless of the programming language being used. This is not necessarily true for other data types, which are frequently language dependent.

Character data is also the easiest to translate between different machine representations. MQSeries, for example, can perform these translations automatically, on behalf of applications. This translation takes account of the differences in representation that occur to support different national languages as well as different encodings. MSMQ directly supports Unicode, a representation of characters that can encode all symbols used in the world's languages. As a result, messages that contain only character data can often be exchanged between different systems without the application needing to become involved in issues of translation.

The second rule is to avoid null terminated string representations of character data, such as those commonly found in C programs. Using fixed length character arrays without terminating characters is much more portable, though requires a little more work in C programs.

The Example Application Programs

In the next few chapters we'll be looking at specific examples of messaging applications, written using both MQSeries and MSMQ. Wherever possible we've tried to implement exactly the same application logic so that you can compare the implementations. The examples are based on the design patterns that we have discussed in this chapter. We've tried to make them realistic enough that you get a real sense of how a production application will look, but simple enough that you can follow the key elements of the implementation.

Configuring MQSeries and MSMQ for the Examples

The examples assume that you have either a working MQSeries system or a working MSMQ system. Ideally, you will have both. All the example code is on the companion CD and can be installed on your own computer.

We cover the MQSeries and MSMQ configuration needed for each example in the relevant chapter. If you have your own system, you will be able to perform the configuration operations yourself. If you are sharing a system with others, you may need to work with your system administrator to have the work done. Both MQSeries and MSMQ provide security features to prevent unauthorized staff from modifying their configuration. Obviously, this is crucial for the protection of production systems and for protecting development and test systems that are shared.

 

  • + Share This
  • 🔖 Save To Your Account

Related Resources

There are currently no related titles. Please check back later.