When Microsoft reinvented itself a few years back as The Internet Company, it began to focus a lot of energy on modifying its operating systems and development tools to support distributed applications. While most of the initial effort was focused on Web and Internet applications, the company's technology improvements can benefit all distributed applications, running across any kind of network. Windows 2000 is in many ways the culmination of those efforts, providing services for component-based applications.
But the world is increasingly disconnected, as laptops and PDA (personal digital assistant) devices hit the road, severed from any kind of connection to the net. These devices and their portability are demanding new application designs, letting the user use server-based applications while disconnected, as easily as a standalone, desktop application. Working on a Microsoft Word document while on the road is one thing, but having access to a slice of corporate data, entering orders, and updating core data presents a far greater challenge.
Microsoft first introduced an infrastructure for queued, inter-application messaging in Microsoft Message Queue (MSMQ), which shipped a couple of years ago with the Windows NT 4.0 Option Pack. MSMQ lets applications communicate across the network by packaging together data and instructions. The beauty of the system is that it supports transactionsat some additional processing cost and complexity, of courseso that an application can be assured that a message is delivered exactly once or receive a failure message if delivery proves impossible. Figure 1 displays the high-level architectural model of MSMQ. One problem with this scheme is that each application that participates in a message exchange has to marshal the data in custom formats. There is only the simplest concept of standard message formats in MSMQ.
| Figure 1|
Figure 1: Microsoft Message Queue (MSMQ) provides a model of delayed gratification for inter-application communication. It is built into the component services of Windows 2000, so that the operating system has the overhead and responsibility of managing messages.
Queued Method Calls
Such a scheme began to enable developers to build robust applications that were disconnected from a server or had to operate reliably over an unreliable network. Distributed COM (the distributed edition of Microsoft's foundational Component Object Model) has long allowed an application to instantiate COM objects on other machines across the network, but such calls are synchronous, requiring that both the client and server components be in memory simultaneously and connected. This is the aging client/server model of application communication.
Microsoft has relieved these shortcomings with queued components in Windows 2000, part of the new operating system's component services. Queued components blend the features of DCOM and MSMQ to allow queued method calls on COM objects. A classic example of queued components is a client order entry system that sales people take to the field on laptops. As they make their rounds during the day, they can run an application that calls a server object's AddOrder method, providing all of the information needed to complete the procedure as arguments to the method. The server object's interface is available to the client application, even though the actual COM object doesn't exist on the disconnected machine. The next time the laptop is connected to the network, the queue features in the operating system handle communicating the messages that optionally instantiate and invoke the method calls, complete with marshaled data.
The Queued Components architecture consists of four objects, all managed by component services in W2K: Recorder, Listener, Player, and Message Mover. The first three are shown in the Component Services MMC snap-in in Figure 2. When a client calls a queued component, the call is made to the Recorder object, which packages it as part of an MSMQ message and puts it in a queue, recording all of the method calls for the queued component. The Recorder selects interfaces from the queueable interfaces described in the COM+ catalog of the server object.
| Figure 2 |
Figure 2: The Component Services MMC snap-in is the central administration tool for all component services in Windows 2000, including queued components. You can manage three of the primary queued components objects here: QC.DLQListener, QC.ListnerHelper, and QC.Recorder.
On the server side, once the client computer reconnects to the network, the Listener object retrieves the message from the queue and passes it to the Player. The Recorder marshals the client's security context into the message and the Player unmarshals it at the server side before making the call. The Player then invokes the method call on the server component.
Queued components have a fail-safe system for times when a message just can't be delivered, when it becomes a poison message. The transaction is rolled back and the message goes to the top of the queue, potentially causing an infinite loop unless something is done to correct the problem. After several retries, the message is moved to a final resting queue. Messages stay here until they are manually moved by using the Message Move object, an automation component that transactionally moves failed messages from one queue to another so they can be retried, a process that you can control programmatically.
Such a scenario doesn't come free, of course, and if you want server-side COM objects to support queuing you'll have to live with some development constraints. The first requirement is that all communication with the queued component be through method calls with no return values. Using method calls only isn't all that onerous a requirement, since if you have been developing components for Microsoft Transaction Server (MTS) in scalable applications, you've gotten used to avoiding object properties and long argument lists on methods. But banning return values is a stickier situation, requiring that you implement a queued component on the client side that the server component can use to return data and indicate the success or failure of a method call. In essence, you have to create multiple components on both the client and server sides that work together but each only support one-way communication.
Complicating application installation is the need for MSMQ to be installed on both the server and client machines. For disconnected clients, MSMQ must be installed as an independent queue, which requires some work to get set up properly. But the messaging enhancements in W2K have made this an easier task.
Because of the delayed messaging nature of queued components, all arguments to the method calls must be passed by value. Passing data by reference requires a pointer to a memory location, something that won't exist by the time the server processes the method call.
One complex issue that I haven't yet thoroughly explored is passing objects as an argument to a queued component's method call. The queued components system takes care of marshaling input arguments, and handles basic data types such as integers and strings efficiently. But complex data types need help. When you pass an object as an argument, the Recorder object marshals the object into an MSMQ message and passes it to the Listener object, which unmarshals the object and re-instantiates the object before passing it to the method call. All such objects passed as arguments must support the IPersistStream COM interface or an error will be raised.
Components built with Visual Basic can support the IPersistStream interface and so can be queued. ADO server-side recordsets do not support the interface and so cannot be queued, but client-side recordsets do support it and so can be queued. These kinds of issues will serve to complicate application design.
Buying Into the Benefits
Distributed application design is a complex undertaking, and throwing queued components into the mix only makes things more convoluted. But for many applications, they can move a large body of nasty code to the operating system, squarely where it belongs. Queued components are a solution when the server component is not always available, whether through maintenance downtime, an unreliable network, or disconnected applications. I've written too many applications that generated some kind of data file on the client that had to be imported into a server application. The results of such a technique are rarely satisfying for the end-user, so I'm eagerly looking forward to converting these apps.
Another advantage is that server processing can be much more efficient, because a component is able to process many messages in a kind of batch mode, saving the overhead of repeatedly instantiating and destroying an object, often a very slow process. A related benefit is that server processing can be scheduled for off-peak periods, when resources are more available. Finally, the transactional capabilities of queued components, built on the MSMQ transaction features, provide the foundation for reliable distributed applications.
As sweet as these benefits can be, a queuing component design is not a panacea and won't fit every application. Queued components can add significant overhead to an application and network, and they don't provide a viable solution when the user needs or expects immediate feedback from an application. Further, MSMQ is not a simple system to install or administer, and the task gets ever more complex as the size of the enterprise messaging system increases. Using transactional features forces changes to code, making it harder to maintain an application that needs to operating in both synchronous and asynchronous environments. The moral is that queued components can be put to good use in distributed applications, but you'll pay the price for those benefits, so use them only if you need them.