Introduction to Reliable Messaging with the Windows Communication Foundation

 

Clemens Vasters
Microsoft Corporation

July 2006

Applies to:
   Microsoft Windows Vista
   WS-ReliableMessaging specification

Summary: In this article, Clemens Vasters explains how reliable messaging and session support works with the Windows Communication Foundation (WCF). You will learn about the WCF implementation of the WS-ReliableMessaging specification, how and why reliable delivery and sessions are related, and whether and to what extent the WCF standard bindings support reliable messaging. (14 printed pages)

Contents

How (WS-) Reliable Messaging Works
Reliable Messaging (and Sessions)
Reliable Messaging with Standard Bindings
Demanding Reliable Session Support
One-Way, Queuing, and Durable Messaging

Reliable messaging is the key enabler for implementing powerful software architectures that allow for robust, concurrent processing of data and therefore greatly enhance scalability. In addition, reliable messaging is the foundation for writing applications in a world where the opportunities to get connected increase day by day, but where the quality of network connectivity is often anywhere from barely acceptable to very poor.

Big words. Really? How so?

The new InterCity Express railway between Frankfurt and Cologne in Germany, which has been dug into and elevated above the hills alongside the eastern bank of the Rhine river, has been built as a replacement for a gorgeous scenic route winding alongside Europe's most important waterway. The new track is 30 miles shorter, has cut travel time by an hour, and features 18 major viaducts and 30 new tunnels—each of them equipped with a significant investment in mobile phone repeater technology. But this new track, which has been operational only since 2003, is still exceptional in that regard.

Smart-client applications used by a mobile workforce have very different communication requirements compared to in-house desktop applications. Users dial into the corporate network through VPN links that are relayed via mobile data services such as GSM/GPRS, 3G/UMTS, or WiFi connections—and quite often, "mobile" is truly synonymous with "moving." And even in Germany, with its excellent telecommunication infrastructure, the quality of connectivity on a train or in a car is very often dependent on the landscape—there are many tunnels in Germany's railway system and on Germany's streets and more than a few of them cause your mobile link to collapse. For building mobile, connected applications that need to fetch information from and send data to back-end systems, these real-life connectivity constraints are a substantial challenge. If an application is using "basic" Web services with plain HTTP and your communication link could very realistically drop any second, it is often not easy to tell whether the data your application's user has been entering into the client application over the last 30 minutes and that was just submitted has indeed been understood and stored by the back-end system. And in case you do find that you have a network failure condition—when and how do you retry? Even worse: What do you do if you need to relay a batch of information consistently and in a particular order and the communication link fails intermittently?

"Occasional connectivity" in mobile applications is one of the driving motivators for reliable messaging in Web services. Another one is very simply the occasional network congestion occurring anywhere on the Internet.

While many need and want the openness, platform-independence, and ubiquity of HTTP-based Web services, the inherent lack of transport reliability of HTTP is very problematic for scenarios such as the one described. The transport-independent WS-ReliableMessaging (or WS-RM) protocol that has been submitted to OASIS for standardization and which is implemented by the Windows Communication Foundation allows for creating reliable communication paths over unreliable connections and protocols. It does so by establishing end-to-end communication sessions and by introducing explicit acknowledgement of messages into the communication flow.

How (WS-) Reliable Messaging Works

Even though he Windows Communication Foundation does – as you will learn – conveniently hide all the intricate details of the WS-ReliableMessaging specification from the application developer and makes the implementation of reliable messaging a mere matter of picking the right binding configuration for a service and service client, it is certainly useful to have a bit of an idea of what's going on behind the scenes and on the wire.

After all, you can only really trust a mechanism that you fully understand, right?

Reliable messaging works generally like this: A client sends a sequence of messages (the sequence may be as short as one message) across a communication link and asks the receiver to acknowledge that it has received the message(s).

The acknowledgement(s) are sent back to the client either individually and for each message, or in a single acknowledgement for a series of messages. Once the client has received the acknowledgement, it knows that the message(s) has been successfully transferred.

The TCP (Transmission Control Protocol, as in TCP/IP) works with acknowledgement messages to make sure that all data packets of a sequence are reliably transferred between two endpoints. WS-ReliableMessaging works very much in the same way – just a bit higher up on the communication stack and independent of the underlying transport. More concretely: WS-RM is designed to control reliable delivery of single SOAP messages or sequences of SOAP messages between two endpoints, irrespective of how these endpoints are connected; for instance even if messages travel through message routers or other intermediaries using different transport protocols for each of hop.

To establish a reliable messaging link between two endpoints, we first need a notion of a connection or "session". Each message sent in the context of that session is assigned a unique number denoting its order in the sequence. The sender-side (or "initiator") establishes a temporary cache to keep track of the messages sent and matches them up with the incoming acknowledgements. If a message is not acknowledged after a certain time (the "retry-timeout"), the message is resent automatically from the cache. Once an acknowledgement is received, the message can be removed from the cache.

The receiver-side (or "acceptor") establishes a cache for holding messages it accepted in order to store them before delivering them to the application. That's done because the receiving infrastructure and the application may have a "pull-relationship".

Deep down in WCF's channel architecture where the WS-RM channel is stacked on top of the transport channel and possibly some additional channels such as the security channel, arriving messages are queued up in the transport channel as they arrive.

So – and this is indeed a bit of a simplified description of how it all works – whenever a worker thread becomes available to process a message, the service model pulls a message from that transport queue through the channel stack (each channel pulls from its underlying channel) and dispatches the message into the services you write. It may look as if every message is immediately and synchronously pushed to the services you write, but in fact there is always such a pull-push translation taking place.

Because there may be a delay between the arrival of the message and the message dispatch depending on how busy the service is and because the RM channel is concerned about acknowledging arrived messages rather quickly, the RM channel will not wait for messages to flow by as they bubble up from the transport to the service model. Instead, it proactively pulls them out of the underlying transport queue as they arrive, sends out the necessary acknowledgements (if it can; we get to that later) and keeps the messages available in its own queue for pickup by the service model layer.

This cache (or queue) also serves to temporarily hold any messages received out-of-order so that messages can be delivered to the application in the order they were sent. If messages 3 and 4 of a sequence arrive before message 2, these "later" messages are held in the cache until message 2 arrives and therefore only delivered to the application once the sequence is in proper order. This happens if the application requires ordered delivery and message order enforcement is turned on (which is the default behavior).

In addition for supporting ordered delivery, the message number allows the receiver-side to detect duplicate messages and discard them. Messages can be duplicated on the path from the sender to the receiver, or be sent twice by the sender-side if an acknowledgment is lost or delayed.

It's important to note that the terms "initiator" and "acceptor" are used instead of "client" and "server", because each end of the communication path can and often will play both roles. The "client" and "server" terminology becomes easily confusing once you start to think about bi-directional communication.

If we assume having a Request/Response communication pattern, the response needs to be delivered just as reliably as the request and therefore the responding party must implement an initiator mechanism that is very similar to what the requesting party implements for the original requests. The requesting party, in turn, is playing the acceptor role for the responses. If responses get lost, they must be resent by the responding party and therefore they must also be cached (and acknowledged). Both ends of a reliable messaging session therefore maintain separate caches for outbound and inbound messages.

Whenever the communicating parties have a bi-directional (dual) communication link, the responding party can simply retry sending messages just like the requesting party would retry sending if the request cannot be delivered.

However, if the communication link doesn't allow the responding party to deliver messages independent of a client request, things get a bit more complicated. That is specifically the case with HTTP where the responding party only has an opportunity to get anything back to the requesting party when the latter comes around with another request.

To force the requesting party to come around and pick up unacknowledged responses when using HTTP, the responding party uses a simple trick: The acknowledgement for the request message is always piggybacked on the response. With that and in case of the response getting lost, the requesting party will not receive an acknowledgement and will resend the request, even though the original request might indeed have arrived at the responding party and the request was already processed. The responding party will detect such a resend request by inspecting the message's sequence number and will serve the cached response resulting from the original request from its message cache instead of re-processing the message.

Any acknowledgements for received responses are piggybacked on subsequent requests made on the same reliable session, so that the responding party can clean up its response cache. The request that carries the response acknowledgement is thereby completely unrelated – its message simply serves as the next convenient ferryboat to come along and cross the river.

The scope for a WS-ReliableMessaging session is called "Sequence" and is established by a "CreateSequence" Web service request that is sent out-of-band between the communicating endpoints. Out-of-band means that this particular request is sent and handled by the infrastructure and not surfacing as a web service call anywhere on the service model level. Once the party initiating the sequence is done with its work and has collected all outstanding acknowledgements it sends a "TerminateSequence" request – also out-of-band and "behind the scenes" as the channel is closed.

If you really were to poke around down below in the channel architecture, you would see that receiving the "CreateSequence" request causes a new channel to become available, through which all messages for the respective sequence are delivered. The "TerminateSequence" message will cause that channel to indicate the end of the sequence by delivering a null message to the receiver.

Reliable Messaging (and Sessions)

Given the discussion up to here it should not be very surprising that the Windows Communication Foundation provides WS-ReliableMessaging support through a binding element named "reliable session" (concretely: the ReliableSessionBindingElement class and the reliableSession configuration section) where session support and reliable messaging support go hand-in-hand.

Also, because reliable messaging requires bidirectional information flow to relay acknowledgements, it is incompatible with one-way datagram transports such as UDP (which is not directly supported by WCF, but for which a transport channel sample exists in the WinFX SDK) or the Microsoft Message Queue (MSMQ) – whereby the latter is slightly different reliable transport option that we will discuss further towards the end of this article.

<customBinding>
   <binding configurationName="customReliableHttpBinding">
      <textMessageEncoding messageVersion="Default" encoding="utf-8" />
      <httpTransport />
   </binding>
<customBinding>

The binding configuration snippet above is a very simple custom binding that supports HTTP transport and encodes messages as UTF-8 text with the default (SOAP 1.2 and WS-Addressing as of 10/2004) message format version.

Adding reliable messaging support to this binding is as simple as adding another binding element (underlined):

<customBinding>
   <binding configurationName="customReliableHttpBinding">
      <reliableSession ordered="true" />
      <textMessageEncoding messageVersion="Default" encoding="utf-8" />
      <httpTransport />
   </binding>
</customBinding>

Just to show how additional options can be configured in configuration and in code (see below), the binding element that we're adding here also explicitly requires ordered message delivery, which is on by default. The equivalent binding composition in code would look like this:

// create and configure transport
HttpTransportBindingElement httpTransport = 
      new HttpTransportBindingElement();

// create and configure encoder
TextMessageEncodingBindingElement textMessageEncoding = 
      new TextMessageEncodingBindingElement();
textMessageEncoding.Encoding = System.Text.Encoding.UTF8;
textMessageEncoding.MessageVersion = MessageVersion.Default;

// create and configure reliable session
ReliableSessionBindingElement reliableSession = 
      new ReliableSessionBindingElement();
reliableSession.Ordered = true;

// compose into binding
CustomBinding reliableHttpBinding = 
      new CustomBinding(
            reliableSession,
            textMessageEncoding,
            httpTransport);

With the binding element present, WCF will stack the reliable session listener infrastructure on top of the HTTP transport listener once the service's ServiceHost is opened and bound to its endpoint. Likewise it will stack the reliable session channel infrastructure on top of the client-side HTTP transport channel when a ChannelFactory is opened and therefore bound to a remote endpoint address – note that ProxyBase and ProxyBase<T> are just convenience wrappers around creating, configuring and opening a ChannelFactory and a generated client channel.

Aa480191.introtowcfreliablemessaging1(en-us,MSDN.10).gif

Figure 1. Establishing a reliable session in WCF

This very simplified diagram illustrates how a reliable session is established in WCF. Whenever the first application-level message (1) is sent via a proxy, or a typed or untyped channel bound to a transport address using a reliable session enabled binding, the message goes through a reliable session output channel.

Because there is initially no session present, a new session is created on the sender side (1a) and the session object itself proceeds to send (2,3) an out-of-band message "CreateSequence" to the remote endpoint. At the remote endpoint, the message is accepted by the transport listener and a new transport channel is established. Subsequently the message is forwarded (5) to the reliable session listener. The request in processed by creating a new session (5a) and the appropriate response is sent (7) to the requester, where it is dispatched back to the session object (8), completing the sequence creation roundtrip. Subsequently (9), the application message is forwarded to the output transport by which it is sent (10) to the remote endpoint. At the remote endpoint, the message is handled by the input channel associated with the session, forwarded (11) to the reliable session input channel and finally dispatched (12) on the service instance. A possible response of the service instance would then traverse the reliable session output channel (13) and the transport output channel (14) on the responding side, be sent to the transport input channel at the requester (15) and would then bubble up to the reliable session input channel (16) and finally get up to the proxy (17).

The way by which transfer acknowledgements ("acks") are sent depends on the specific choice of transport channel. If the transport channel is "dual", meaning that there is a bidirectional transport connection between the two endpoints for relaying message traffic in either direction, acknowledgements are sent out-of-band via specific messages and typically in batches. If the transport channel is of a request/reply type, acknowledgements are piggybacked on the next following message (request acks on replies, reply acks on requests).

Sessions and HTTP

For the connectionless HTTP transport bindings the reliable session binding element is the prescribed way to add session support for service contracts.

Because HTTP is connectionless and requests are only initiated by one party, logical connections (in other words: sessions) are typically constructed using reference tokens (cookies) that are shared between the communicating parties alongside each request and response.

The required mechanism on the HTTP level is described in RFC2965, not in the core HTTP specification RFC2616 and it is strictly optional for both parties to support. Unlike ASP.NET Web services where such cookie-based sessions can be enabled on a per web-method basis for a sequence of calls, WCF supports this behavior only on a per-endpoint basis through a configuration switch on the HttpTransportBindingElement at this time.

To enable cookie-based session support, you must construct a custom binding in configuration or code, and set the AllowCookies property or the equivalent allowCookies configuration attribute to true. In a Community Technology Preview (CTP) following Beta 2 and in the final WCF product this option will also be easily accessible on the standard BasicHttpBinding and WSHttpBinding bindings in code and configuration and you will no longer have to construct such a custom binding.

However, there are a number of reasons for not using cookie-based sessions for HTTP-based Web services:

  • Both parties can always (and legitimately as per RFC2965) opt-out of a cookie-based session mechanism by dropping the cookie and the mechanism is therefore not very dependable
  • There is no standard way to for a service to express the requirement for cookie-based session support to the client via WSDL or WS-Policy.
  • HTTP-level cookies cannot be digitally signed like message headers (unless SSL is used, which is not always an option) and therefore pose a potential security threat allowing sessions to be stolen and spoofed by third parties.

Consider the following, imaginary, session-enabled contract as an example; it allows server-side composition of a financing package with several loans, retrieving status such as the overall average interest, fees, and combined monthly payment and finally the submission of the composed package to the bank's internal loan application processing:

[ServiceContract(Session = true)]
public interface IFinancingPackageBuilder
{
   [OperationContract(IsInitiating=true)]
   LoanId AddLoan(LoanInfo loanInfo);
   [OperationContract(OneWay=true,IsInitiating = false)]
   void RemoveLoan(LoanId loanId);
   [OperationContract(IsInitiating = false)]
   PackageStatusInfo GetFinancePackageStatus();
   [OperationContract(OneWay=true,IsTerminating = true)]
   void SubmitApplication(PersonalInfo personalInfo); 
}

As you can derive from the above service contract example, it is quite common that services which indeed need session support do immediately benefit from reliable message delivery, because they might have actions that are not idempotent.

"Idempotent" means here that the resulting state of the service will be the same, irrespective of whether you invoke the same action once or twice or any number of times.

That's not necessarily true for the AddLoan or RemoveLoan operations shown above, which incrementally add and remove data to and from the service state. Non-idempotent actions can cause quite a bit of a headache in the case of message loss or duplication. Hence, combining sessions and reliable messaging doesn't only have a technical reasoning on the infrastructure level as explained earlier, it also makes a lot of architectural sense on the application level.

In contrast to HTTP, the connection-oriented TCP and Named Pipe transports inherently support sessions. For both, an implicit session exists automatically as long as the connection is kept alive between the endpoints. Adding transport-independent reliability via the reliable session mechanism is often useful, but not required for session support as with HTTP.

The main benefits of using reliable sessions on top of these transport-inherent session capabilities are end-to-end reliable message delivery in routing scenarios and added resiliency to temporary connectivity failures.

Reliable Messaging Session Options

The reliable session binding element and its underlying infrastructure have a few configuration properties that are listed in the following table. Each of these settings affects only the local behavior of the configured communication endpoint – that means that a client of a request/reply communication may have settings that differ from the respective service side.

Mind that all mentioned default values are preliminary for Beta 2 and might change in the final product.

Property Description
AcknowledgementInterval
(TimeSpan)
Interval that the recipient shall wait until sending acknowledgements for messages. The default is 2 seconds. Messages received within the configured interval time span are acknowledged in one batch, with the goal of reducing network traffic. The first message in a session is always acknowledged immediately. The setting is a recommendation to the infrastructure, not a hard limit.

Note: This setting only applies to bidirectional duplex transport bindings and does not have any effect on request/reply style bindings where each request message is acknowledged on the respective reply and replies are acknowledged on the next request.

EnableFlowControl
(Boolean)
Turns on or off the flow control feature (this feature is on by default). This feature helps the sender avoid wasting network resources by stopping sending messages when the receive-side buffer for incoming messages is full. Messages sent when the buffer is full will be discarded and will require retransmission, so avoiding sending these messages will help conserve network resources. This optimized use of network resources is enabled through reporting the available capacity on the receiver side's buffer to the sender, such that the sender won't send messages if the buffer is full.
MaxTransferWindowSize
(Int32)
This value specifies the number of messages that can be held in the local message buffer for each reliable session. On the sender side this buffer would hold messages that were not yet acknowledged and on the receiver side this buffer would hold messages that were not yet processed by the application. The default for this value is 32, the minimum is 1 and the maximum is 4096. When the sender buffer size fills up to the configured limit on a session, the sender is blocked. When the receiver buffer size fills up to the limit on a session, incoming messages are dropped.
InactivityTimeout
(TimeSpan)
The inactivity timeout, which defaults to 10 minutes, defines the length of time that may pass without receiving messages from the other side (be they application messages, acknowledgments, or other infrastructure messages). When no application-level messages are being sent, the infrastructure would start sending "keep-alive" messages to verify that the connection is still valid. If no messages are received within that time limit, the session faults.
MaxPendingChannels
(Int32)
This setting controls how many pending requests for new client initiated sessions are kept in the "pending channels" list. Whenever a client tries to establish a new session, the channel is created on the service side and must be accepted and opened by the reliable session listener for the session to be established. Under pressure there may be more requests for new sessions piling up than the infrastructure can handle.

The default maximum value for pending, as-of-yet-unaccepted channels is 128. Once this threshold is reached, the receiver infrastructure rejects requests for new sessions.

MaxRetryCount
(Int32)
This value, which defaults to 8 (minimum 1, maximum 20), specifies how many times the infrastructure shall retry to resend a message in case of a transmission failure. Once a message has been unsuccessfully resent for the configured number of retries, the failure is considered to be unrecoverable and causes the channel to fault.
Ordered
(Boolean)
If this configuration property is set to true (which is the case by default), the receiver side infrastructure will dispatch all messages in the exact order they were sent. If messages arrive out of order they are buffered until the missing messages of the sequence arrive. If this is set to false, messages are immediately dispatched onto the service, irrespective of the order in which they were sent.

What's truly surprising about this list of configuration properties is that while the number of retries (MaxRetryCount) is configurable, the interval in which retries are attempted isn't showing up on the list of configurable values.

This is so because WCF applies some sophisticated congestion control algorithms to the reliable transmission operation that go far beyond a simple retry timeout. The algorithms used here are similar to the tried and proven congestion control mechanisms employed by most TCP implementations. Generally speaking, the retry timeout value is initially rather generous and the number of messages that can be left unacknowledged while the sender keeps sending additional messages starts very low, typically at 1. If the communication path proves to be quick and reliable and no messages loss occurs, the timeouts are shortened and the sender will tolerate an increasing number of unacknowledged messages, expecting that the acks will come around eventually. If a message loss occurs, the shorter timeout will cause a quicker resend. Allowing for more outstanding acknowledgements aids higher throughput. Should the quality of the communication path decrease and become very slow or even occasionally disconnected, the algorithm causes the timeouts to increase exponentially and the allowed number of outstanding acknowledgements to be substantially reduced, which throttles the throughput and increases the failure tolerance.

Reliable Messaging with Standard Bindings

Whether and how WCF's standard bindings support sessions and reliable messaging depends very much on the underlying transport's inherent capabilities and on whether reliable messaging is implicitly required by the binding to support its functionality.

Binding WS-RM Support Session Support
BasicHttpBinding None No
WSHttpBinding Yes, if the ReliableSession.Enabled property is set to true on the binding element or in configuration. Yes, if RM is enabled.
WSDualHttpBinding Always, Implicit Yes, Implicit
NetTcpBinding Yes, if the ReliableSession.Enabled property is set to true on the binding element or in configuration Yes, Implicit
NetNamedPipeBinding None, reliable delivery guaranteed by underlying transport Yes, Implicit
NetMsmqBinding None, reliable delivery guaranteed by underlying transport Yes, if transactional queues are used.
MsmqIntegrationBinding None, reliable delivery guaranteed by underlying transport No

The BasicHttpBinding does not support any of the WS-* headers with the exception of the basic WS-Security profile and is meant to support simple, HTTP-based SOAP messaging only. Hence, reliable messaging and sessions cannot be enabled on this binding.

The WSHttpBinding and the NetTcpBinding are WS-* aware bindings that flow request messages (and possibly replies) from a sender to a recipient. Both bindings allow reliable messaging to be enabled by setting the ReliableSession.Enabled configuration property to true.

Sessions are implicitly supported by the connection-oriented NetTcpBinding's underlying transport and independent of this setting, while enabling reliable sessions is required for session support with the WSHttpBinding.

The explicit duplex binding WSDualHttpBinding, which establishes a bidirectional, conversational link between two HTTP endpoints, requires reliable messaging and sessions to work. Therefore, RM is implicitly and always enabled for this binding.

With the aforementioned reliable-session capable bindings, the message order will always be enforced – meaning that messages will be delivered to the receiving service in the exact order they were sent. In the unlikely case that you would want to turn this off, you would have to compose a custom binding – as shown earlier on – and disable this mechanism with the Ordered property (or equivalent configuration attribute) on the ReliableSessionBindingElement.

The NetNamedPipeBinding sits on top of the Windows operating system's support for reliable message delivery and reliable streams through named pipes. Because named pipes are connection-oriented, readily support sessions, are reliable by design, and are typically not bridged, there is no need for WS-RM support in this binding.

The Microsoft Message Queue (MSMQ) is an operating system level reliable transport that does not require WS-RM support to implement reliable message delivery. Instead, the NetMsmqBinding has a special set of configuration properties reflecting the transport assurances MSMQ can provide for the delivery of messages.

Even though MSMQ is strictly a one-way transport, it also supports sessions through its transaction support. When the underlying queue is transactional and a set of messages is sent to the recipient service from within a transaction scope, all messages that were queued inside the transaction are delivered to the recipient service within a session scope. Once the last message from the transacted message batch is delivered, the session is closed.

Demanding Reliable Session Support

When coding your application, you may make some assumptions based on the availability of reliable messaging and session support. This makes the presence of these features instrumental to the proper functioning of your applications and they are yet purely a matter of picking and configuring the right bindings. This leaves your application's proper functioning at the mercy of the person who is configuring the system, and that might lead to issues that are incredibly difficult to diagnose and debug.

To make sure that a service whose proper functioning depends on reliable messaging and especially on the ordered delivery of messages works as it should, you can explicitly demand ordered delivery support from the used binding in your application code via the DeliveryRequirementsAttribute:

[DeliveryRequirements(RequireOrderedDelivery=true)]
public class FinancingPackageBuilder : IFinancingPackageBuilder
{
   ...
}

If the configured binding does not support the feature(s) required by this attribute, the service host will refuse to host the service and throw an exception. Demanding session support from a binding is implicitly done by declaring the session requirement on the contract declaration's ServiceContractAttribute:

[ServiceContract(Session = true)]
public interface IFinancingPackageBuilder
{
...
}   

One-Way, Queuing, and Durable Messaging

"Reliable messaging is the key enabler for implementing powerful software architectures that allow for robust, concurrent processing of data and therefore greatly enhance scalability." That's one of the first sentences of this article, but where's the beef?

Well, once you have reliable message delivery consistently available on any transport of your choosing as part of your technology arsenal, whereby it isn't really relevant whether the reliable delivery support is implemented with WS-RM or is inherent to the transport, you can build much more powerful applications, which use implementation patterns that were out of reach for most software developers before WCF due to the complexity or cost of the required plumbing code. It's not that these things couldn't be done before – it was just very hard for people whose primary concern is to implement business functionality for their customers.

Assume you write client application that prepares and submits "money equivalent" documents to a backend application. Let that be purchase orders, accounting records or someone's travel reimbursement filings. Typically, your logic prepares such records and validates them for consistency and then sends them off to the backend system for further processing. And also very typically, your application is "done" with its part of the processing chain after you have prepared them and a notification about the result of the processing will – if at all – only get back to you much later and possibly through different channels (maybe by ways of an email).

With reliable messaging in place, your client application can entrust the infrastructure with getting messages to their destination, mostly irrespective of the networking conditions. If you combine this with a local, sender-side, persistent queue for the jobs to be transmitted ("Queue" isn't synonymous with MSMQ, it's just with WCF that MSMQ is supported out of the box; if you want to write your own queue channel then a local SQL service instance is great for this and the file system, including isolated storage, is good too), you are also reasonably safe for the rare cases where reliable messaging connection fails permanently or where the application or computer crashes. On the receiving end, queues are great to decouple the message transfer from processing. Once you have a message "safely" on your end, you can store the job to a local, persistent queue and have the actual processing read from that queue. WCF's NetMsmqBinding for MSMQ will do that for you, however with all other bindings such a functionality ("durable messaging") would be up to you to implement. And while this may seem like a glaringly obvious omission of a "must have" feature, there are indeed good reasons for why durable messaging is not readily available in any of the other bindings:

Truth be told, the reliability of WCF's WS-ReliableMessaging-based implementation by itself is "only" as good as it can be for a volatile reliable messaging mechanism that cannot make any assumption about the respective remote partner except for what's expressed in the standards. The WS-RM standard only covers what's going on on the wire because WS-ReliableMessaging is a transfer protocol and it says nothing about what should happen to the message after it is successfully received by the other side. If WCF were to give you a hard delivery guarantee for message delivery, come sunshine, hailstorm, or earthquake for its end of a connection and the remote party is standards-compliant but not especially solid in its implementation, you'd end up with an overall quality of service that's just on or below the level as what WCF gives you now.

If you need end-to-end durable reliable messaging with full support for transactional I/O you need an infrastructure that's in control of both ends of the communication path and as it happens, such an infrastructure is part of the Windows operating system family. If these are your requirements, your WCF binding of choice is likely the NetMsmqBinding. If you are mostly worried about not losing messages under less-than-optimal networking conditions or require session support – which are the more common use cases – the reliable session support in WCF is the right choice and a great leap forward when compared to the previous Web Services stacks.

 

About the author

Clemens Vasters is Community Program Manager on the Windows Communication Foundation team at Microsoft, responsible for connecting the developer and architect community with the product group. Before joining Microsoft in early 2006, Clemens, who is a Microsoft Certified Architect, was CTO of newtelligence AG. Clemens was and still is a frequent speaker at software developer conferences around the world.