Windows Communication Foundation includes concepts which enable the developer to insist on reliably delivering messages in both directions. The mechanism is actually transport agnostic and allows messages to be flown reliably from client to service and replies from service to client.
Underneath, WS-ReliableMessaging (WS-RM) implementation is used. Click here for Microsoft's specification. Content is the same.
WS-RM is based on a concept of a message sequence. You can think of it in terms of a session, although it does not carry HTTP session semantics (remember, it's transport agnostic). A communication initiator (RM source) sends a <CreateSequence> element inside the message body to establish a RM sequence. Service side (RM destination) responds with either <CreateSequenceReponse> or <CreateSequenceRefused>, in cases where new sequence is not welcome.
After the sequence is initialized, there is an additional SOAP Header in messages. It identifies the message number being transferred. The following is a simple example of headers of the first two messages:
<S:Envelope>
<S:Header>
...
<wsrm:Sequence>
<wsrm:Identifier>http://webservices.gama-system.com/RM/Service</wsrm:Identifier>
<wsrm:MessageNumber>1</wsrm:MessageNumber>
</wsrm:Sequence>
</S:Header>
...
<S:Body>
...
</S:Body>
</S:Envelope>
<S:Envelope>
<S:Header>
...
<wsrm:Sequence>
<wsrm:Identifier>http://webservices.gama-system.com/RM/Service</wsrm:Identifier>
<wsrm:MessageNumber>2</wsrm:MessageNumber>
</wsrm:Sequence>
</S:Header>
...
<S:Body>
...
</S:Body>
</S:Envelope>
After all messages have been exchanged and acknowledged, the RM destination sends a <SequenceAcknowledgement> inside the body of the message. RM source (communication initiator) then tears down the sequence with a <TerminateSequence> message.
So, how can this specification be implemented in various technology stacks? Well, WCF implements reliable messaging using the in-memory message buffers. There is no durable reliable messaging support in WCF.
Here's how it works:
- Communication initiator (client) sends a <CreateSequence> message.
- Service side responds with a <CreateSequenceResponse>.
- Client sends 1..n messages and buffers them, while waiting for all acknowledgements.
- Service side can and will dispatch the received messages as soon as possible. Now, there are two options:
- Communication is configured for ordered delivery
First message will be dispatched to the service model as soon as it arrives, noting that it has been processed. Every other message is processed in order. If message number 5 has been processed and the next received message carries sequence number 7, it will not be dispatched until message 6 is received and processed by the service model.
- Communication allows out-of-order delivery
First message will be dispatched to the service model as soon as it arrives, so will all the following messages. Since we did not demand ordered delivery the processing pipeline does not care on when to process the messages. It will process them as soon as they are received. They are acknowledged as soon as possible, but not before acknowledgement interval.
- Service side sends a <SequenceAcknowledgement> message only when all messages have been acknowledged.
- Initiator then stops the sequence with a <TerminateSequence> message.
So, how do we go about enabling WS-RM and realizable delivery in WCF? Simple. Here's the config file:
<wsHttpBinding>
<binding configurationName="myReliableBinding">
<reliableSession enabled="true" ordered="true" />
</binding>
</wsHttpBinding>
The <reliableSession> element has two attributes. The first one, enabled, enables reliable message delivery. The second one, ordered, enables in-order processing by the service model.
One has to acknowledge that the following is true for different WCF bindings:
basicHttpBinding - RM not supported
wsHttpBinding - RM supported, not default
wsDualHttpBinding - RM implicit
netTcpBinding - RM supported, not default
There are a couple of options available for anyone using the custom binding, in regard to reliable messaging behavior:
- acknowledgementInterval specifies the time lapse duration of message acknowledgement. The default is two seconds, while always acknowledging the first message. This gives you an efficient way to group the acknowledgement messages, thus conserving network traffic.
- flowControlEnabled enables message sender to acknowledge the receive buffer on the recipient side. It turns out that every acknowledgement message includes a service buffer status and if this attribute is set, sender will not send messages if the recipient buffer is full, thus not occupying network resources.
- inactivityTimeout defines the absolute duration of a no-message (infrastructure and application messages) session. After the timeout, the session is torn down.
- maxPendingChannels defines how many reliable channels the service can put in its waiting-to-open queue. If there's a significant load, the pending requests can queue up and the service refuses to open a reliable session (think <CreateSequenceRefused>).
- maxRetryCount defaults to 8. It defines how many times the infrastructure retries with message delivery. If the limit is achieved, the channel will become faulted.
- maxTransferWindowSize defines how many messages should be buffered. On service side all messages that have not been acknowledged increase the count. On the client side, all messages which have not received their acknowledgement pair are counted.
- ordered defines in-order delivery. It's values are true and false.
There is some confusion in the wsHttpBinding's <reliableSession> config element. There's an enabled property in the Visual Studio config schema, which should not have any influence on reliable session establishment (and does not have a matching object model method/property). It does, however. There is a difference if you setup a reliable session by using a customBinding or wsHttpBinding.
I.e., here's the custom binding config:
<customHttpBinding>
<binding configurationName="customReliableBinding">
<reliableSession ordered="true" />
</binding>
</customBinding>
This will enable reliable and ordered session support in any custom binding.
So, in general - WCF implementation of WS-ReliableMessaging gives you automatic message retries, duplicate detection, and ordering. It should be turned on for any multi-hop message paths, but can be very valuable even in high latency/packet loss network scenarios.
Update 2007-02-19
William Tay, a friend and distributed systems expert, replies with a terrific message exchange pattern for groking WS-RM specification. Don't walk, run.