WSE 3.0 and WS-ReliableMessaging

 

George Copeland
Architect

June 2005

Applies to:
   Web Services Enhancements 3.0 for Microsoft .NET
   WS-ReliableMessaging (WS-RM) specification

Summary: This article provides an overview of WSE 3.0 Reliable Messaging (WseRM). This is a sample code reference implementation for WSE 3.0 that implements the latest draft of the WS-ReliableMessaging (WS-RM) specification. The article first describes the scenarios where WS-RM is applicable, then provides an overview of the WS-RM specification, and finally it defines the scope and goals of WseRM. Detailed documentation, samples, and the complete source code for the WseRM reference implementation are provided in the associated MSI download. (17 printed pages)

Download the associated sample code, Microsoft WSE 3.0 Reliable Messaging.msi.


Contents

Introduction
WS-RM Specification
Direct vs. Queued RM
Scope and Goals for WseRM
What Is in the WseRM Install
WseRM Programming Model Concepts
A Taste of Programming
RM Implementations
Systems Management
Conclusion

Introduction

Messages that are sent may never arrive, arrive too late, arrive multiple times, or arrive out of order. After messages arrive, the service may crash and lose some messages.

As an example, in a conversation between buyer and seller services, the buyer service may send a message to make a purchase and wait for shipping information. If this purchase message never arrives or is lost by the seller prior to fulfillment, the purchase will not progress. On the other hand, if this purchase message arrives twice, it may result in duplicate purchases.

As another example, say the purchase request includes a first message containing a list of item catalog numbers followed by a message per item with more detail (for example, color, quantity, size). If the first message does not arrive first, the subsequent messages will not make sense. Or if the subsequent messages are out of order, the detailed description may be applied to the wrong catalog number in the first message.

Such problems may be more difficult to deal with because the client is a Web service instead of a human.

Application code can handle these failures with extra code, sometimes requiring the cooperation of the remote endpoints using an enhanced message exchange pattern. However, this approach can significantly increase the investment in the application, as well as its time to market. It can also make the application more difficult and time-consuming to adapt to its changing requirements in the future.

As an alternative, reliable messaging (RM) can be used as part of overall application failure recovery to improve application programmer productivity and time to market.

WS-RM Specification

This section provides a summary of the WS-Reliable Messaging specification (http://msdn.microsoft.com/library/en-us/dnglobspec/html/WS-ReliableMessaging.pdf).

Reliable Messaging in Web Services

To provide reliable messaging, messages must be queued for the following reasons:

  • For output messages, their transmission over the wire may need to be retried.
  • For input messages, their delivery to the application may need to be retried, and their delivery order may not be the same as their arrival order.

The following diagram illustrates two services using a shared queue.

To send a message from application A to application B, A enqueues the message in the shared queue service and B dequeues it from the shared queue service.

The diagram also illustrates two services using private queues. To send a message from application A to application B, A enqueues the message in its private queue, which transmits the message to B's private queue, and B dequeues the message from its private queue. In this case, a service's private queue acts like its post office box. Its public address is to the private queue.

The shared queue approach requires the communicating endpoints to agree on a particular queue service, as well as an enqueue/dequeue protocol. There is no standard protocol for enqueue/dequeue, so the shared queue approach does not support interoperability.

The private queue approach requires the communicating endpoints to agree only on a transmit protocol, so it is more loosely-coupled and more suitable for Web services. The WS-Reliable Messaging (WS-RM) specification provides a standard transmit protocol, so the private queue approach supports interoperability.

The WS-RM Model

The following figure illustrates the basic model and terminology used in the WS-RM specification. It describes a one-way sequence from Source to Destination.

A typical flow is:

  1. The application source requests a sequence to be created with a CreateSequence message. If the application destination accepts, it sends a CreateSequenceResponse. This establishes a sequence identifier for the sequence.
  2. The application source sends one or more messages with an RM header that contains the sequence identifier. This puts each message in the RM source's output queue. Send is a local event—not a distributed event. The last message in the sequence is marked a LastMessage element in the RM header. This can be an application message (with an application Soap Action) or an infrastructure message (with a wsrm:LastMessage Action).
  3. After a message is in the output queue, the RM source transmits it over the wire to the RM destination. Within a sequence, transmitting can overlap with sending.
  4. The RM destination receives messages into its input queue.
  5. The RM destination acknowledges (acks) the messages received so far in that sequence. An ack implies transmission—not delivery to the application. Acks are sequence-specific, not message-specific. Acks are not reliable. If a source does not receive an ack that includes a reliable message, it will eventually retransmit the message. There is no set policy for when to transmit acks. For example, it can be done as each input message arrives, periodically, when the last message arrives, or when an AckRequested message arrives. The later the acks are sent, the fewer ack messages may be needed. The sooner acks are sent, the fewer retransmissions have to be handled.
  6. The RM destination delivers the message to the application destination. Deliver is a local event—not a distributed event.
  7. When the RM source gets acks for the full sequence, it sends a TerminateSequence message. This message is not reliable. If a destination does not receive it, it can resend an ack with the full range.

The WseRM sample and support documentation use these WS-RM specification terms.

The WS-RM specification supports a CreateSequence/ CreateSequenceResponse request-response message pair to allow a service to initiate either:

  • A one-way output sequence.
  • A duplex sequence, which is a pair of one-way output and one-way input sequences.

Establishing a duplex sequence via a single request-response exchange makes it easy for the endpoints to correlate the two one-way sequences, so they can be presented to the application as a single duplex sequence.

There is no support in the WS-RM specification for a service to initiate an input-only sequence. A service can only initiate an output sequence or a pair of input and output sequences. This is reflected in the WseRM programming.

A one-way sequence (input or output) is an ordered set of messages, where the ordering is determined by the sender. It is up to the destination to decide whether the application needs the delivery of the messages to be in-order.

Each one-way sequence has a unique identifier that is established during the CreateSequence/ CreateSequenceResponse exchange. The destination always determines this identifier. This sequence identifier is in each application reliable message in an RM header, and is in each RM infrastructure message (for example, ack, terminate, faults).

Security is accomplished via a secure conversation, which envelopes an RM sequence.

Direct vs. Queued RM

Both the forthcoming product code-named "Indigo" and WseRM have 2 modes of using RM:

  • Direct RM is used when the interaction requires tight interaction of the two endpoints like a phone conversation between two parties. Each endpoint needs the other to be up, reachable, and responsive.
  • Queued RM is used when the interaction is looser with arbitrarily long delays like e-mail. The endpoints do not need each other to be up, reachable, or responsive when the message is sent or received.

When using WseRM, the API object models for direct and queued RM are the same. However, direct and queued RM support different application scenarios that deal with overall recovery differently, so the overall programming models are fundamentally different.

Both direct RM and queued RM are supported in WseRM. The following figure illustrates the characteristics of direct and queued RM.

Direct RM

Direct RM supports conversations between two endpoints that assume close and dedicated interaction with each other. The remote service must be reachable, up, and responsive for forward progress to be made, just like a phone conversation.

Services involved in the distributed application make forward progress and recover as a unit with a synchronized distributed state. A direct RM session cannot last across service failures unless specific action is taken. When system failures occur, the services can recover by either of the following:

  • Application-specific messages can be used by a service to tell other services to go back to a mutually known earlier point.
  • Distributed transactions between the sender and the receiver can be used to simplify application code for recovery. A distributed two-phase commit automatically takes the state of all involved services back to the beginning of the transaction where the result of all messages sent and received is undone.

As illustrated in the above figure, direct RM offers the following guarantees that some transport protocols do not offer:

  • Exactly-once: This ensures that each message is delivered exactly-once.
  • In-order: This ensures that the order that the messages are sent by the RM Source application is the same order seen by the RM Destination application.

Direct RM has the following advantages over transport-level protocols:

  • End-to-end: This provides end-to-end instead of point-to-point guarantees. That is, when an ack is received, the sender knows the ultimate receiver has received the message instead of just the next intermediary (such as a firewall, network router, or load balancer). The ultimate receiver can sign the ack to prove it sent the ack ensuring message-level security.
  • Connection robustness: Direct RM provides a logical connection to the application that survives as underlying transport connections come and go.
  • Transport independent: As with other SOAP-level mechanisms, direct RM provides transport independence, so the same APIs are used across a variety of transports. This allows the transport to be changed without changing the application code.

Queued RM

Queued RM supports conversation between two endpoints that cannot assume tight interaction. The remote endpoint does NOT have to be reachable, up, or responsive for the local endpoint to make forward progress.

A distributed application is typically designed to deal with multiple different remote endpoints. Often, the list cannot be known ahead of time and they may vary widely in their behavior. Some remote endpoints may be able to respond immediately, while others may take longer due to a burst in traffic, temporary disconnection, a slow third party, human approval, or even manufacturing.

Applications using queued RM use smaller local units of forward progress and recovery than applications using direct RM. The arrival of an input message just causes the message to be enqueued, adding to the to-do work for the service. When resources are available to process the input message, a state transition uses a local transaction to dequeue the message, process it, and enqueue output messages, all without involving remote endpoints. After commit, the RM infrastructure begins its attempt to transmit the output messages, only requiring the remote service to eventually become available. These smaller local transacted steps provide the isolation needed to deal with unpredictable responsiveness in remote services.

As illustrated in the figure below, in addition to the advantages of direct RM, queued RM offers the following advantages:

  • Independent progress: As described above, when a remote service is not available, a service can make progress locally. A state transition (forward or backward) takes place without communicating with other services.
  • Fixed cost per message: It can be dangerous to hold expensive resources (for example, memory, threads, data locks) waiting for the next input message. Instead, it is safer for each service in the distributed application to give up expensive resources (deactivate), and reacquire them when the next input message arrives (activate). The safer fixed overhead of disk I/O replaces the danger of an overhead that increases with time.
  • Recovery from service failure: Some remote endpoints may be highly reliable and available (e.g., a server farm with a clustered database). Others may be periodically out of service (for example, a personal machine or small single-machine server). A queued RM session can last across service failures, because RM metadata (sequence information, messages, etc.) is stored in a transacted durable store that can safely store output messages for later transmission and input messages for later delivery after recovery.

The storage used by queued RM is a transactional resource manager, so that output messages can be enqueued and input messages can be dequeued as part of an application transaction:

  • Since enqueue (for send) does not actually happen until the transaction commits, transmission does not begin until after commit, so an abort automatically "takes back" the messages the application sent during the transaction.
  • Since dequeue (for delivery) does not actually happen until the transaction commits, an abort leaves the message in the input queue so it can be retried later in another transaction.

Scope and Goals for WseRM

The goals of the WseRM reference implementation are:

  • To provide a sample reference implementation of the WS-ReliableMessaging specification.
  • To add RM sequences to the WSE 3.0 programming model for applications.
  • To provide an implementation of direct RM.
  • To provide an implementation of queued RM that leverages SQL Server to provide the following in a server farm:
    • Scaleout: Adding more nodes increases throughput.
    • Recovery: When a node fails, committed data is not lost and the service is left in a consistent state.
    • Availability: Every aspect of the service remains functional when a node fails but with reduced throughput.
  • To provide a WseRM Visual Studio 2005 solution with source code, so that you can modify WseRM implementation for your own application needs.
  • To provide samples using WseRM to illustrate the programming model and example use cases.
  • To compose naturally with other WS specifications and WSE 3.0 functionality, such as security and transactions.
  • To provide documentation for the above.

Non-goals of WseRM are:

  • WseRM is not a supported product and has had limited testing. WseRM is a sample reference implementation and provides no guarantees on operational effectiveness or correct implementation. Its main purpose is to enable you to build sample applications so that you are able to appreciate how to apply WS-RM. A fully supported implementation of WS-RM will be available in the released version of Indigo.
  • Although scaleout in a server farm is a goal, high efficiency is not, due to limited development resources. Some target efficiency measures have been made where development time was minimal.
  • No performance or stress testing has been done.
  • Interoperability with Indigo has not been tested.
  • Provide UI tools to manage and monitor the queued RM. Instead, the queued RM schema will be provided to allow any database application to manage and monitor the queued RM. This is more flexible and requires fewer development resources. Some management stored procedures are provided.

What Is in the WseRM Install

This section details what is in the WseRM install associated with this article.

WseRM is installed to the following directory:

...\Program Files\Microsoft WSE\v3.0\Samples\CS\RM

The subdirectories contain the following:

  • \runtime directory: RM infrastructure source code is available as a Visual Studio solution. The directory structure and CLR namespaces are:
    • WseRM: Programming model interfaces and classes.
    • WseRM.Common: These classes provide how RM plugs into WSE (for example, messaging, policy, and pipeline).
    • WseRM.Queued: The queued RM implementation.
    • WseRM.Direct: The direct RM implementation.
    • WseRM.WireFormat: The classes that wrapper the on-the-wire format.
    • \GenerateDB.sql: The Transact SQL script for creating the database tables, indexes, and stored procedures within an application database.
  • \docs directory:
    • WseRMOverview.doc: A description of the WseRM concepts and implementation (identical to this article).
    • WseRMProgramming: A description of how to program with WseRM.
    • WseRMSamples.doc: A description of the WseRM samples and how to install, compile, and run them.
    • WseRMDesign: A description of the design of WseRM.
    • \reference: The reference documentation. These are compiled from the documentation in the runtime Visual Studio solution, which uses the standard C# /// format. To view the docs, double click \reference\Documentation.chm.
  • \samples: WseRM samples. Each sample is a Visual Studio solution with each service as a Visual Studio project. The directory structure is based on the WSE Web service programming models:
    • \ASMX directory:
         PingDirectTcp
         PingDirectHttp
    • \SoapService directory:
         PingDirectTcp
         MultiPingDirectTcp
         PingQueuedTcp
         EchoDirectTcp
         EchoQueuedTcp
         PurchaseQueuedTcp
         PictureQueuedTcp
    • \SoapReceiver directory:
         EchoDirectTcp

WseRM Programming Model Concepts

This section describes the programming model concepts when working with WseRM. The following diagram illustrates the concepts involved in WseRM and the relationships between them.

Service Space

A service typically has a few endpoints, one for each type of communication with a different type of remote service. Each endpoint typically has a WSDL and WSE policy associated with it to describe how a remote service can talk to it. For example, a seller service might have one local endpoint for buyers and another for shippers. WseRM extends the WSE 3.0 policy infrastructure, enabling you to provide and configure RM support in the WSE 3.0 policy file.

A service typically has many service instances, each holding state for a particular instance of what the service provides. For example, a seller service might have state for each purchase, including a sequence with a buyer service and a sequence with a shipper service.

RM Sequences

A WseRM one-way sequence corresponds to a WS-RM sequence.

A service can have multiple:

  • Input sequences: A one-way input ordered set of messages.
  • Output sequences: A one-way output ordered set of messages.
  • Duplex sequences: A pair of input and output sequences.

A service can initiate:

  • Output sequences.
  • Duplex sequences.

Each sequence has an ID established immediately upon local creation. For a duplex sequence, the duplex sequence and its underlying input and output sequences all share the same ID. These IDs are vital to queued sequences, because they are used by an application to durably relate the sequence to their application. An application can durably store away the sequence ID and later use it to activate the sequence. This ID is used only locally. It does not go out on the wire. The application can create this ID or allow the RM infrastructure to generate a Guid-based ID.

Each input and output sequence has a WsrmID that is used on-the-wire. A duplex sequence does not have a WsrmID. A duplex sequence is simply a pair of input and output sequences that were created together and are used together in a two-way conversation. The input and output sequence that are part of a duplex sequence have different WsrmIDs. A duplex sequence is a convenient way to maintain for the application the original correlation of the pair that the WS-RM protocol provides.

A sequence can also be direct or queued. The API for these are the same. However, their usage in an application is quite different (as described earlier). Their implementations are also quite different. For queued RM, the RM infrastructure propagates a transaction to an application when an input message is dequeued (that is, delivered), and the application propagates a transaction to the RM infrastructure when an output message is enqueued (that is, sent). Transaction propagation is done implicitly via Transaction.Current.

An input sequence can optionally be delivered in-order. In-order means that messages are delivered to the application in the same order that the sender sent them. For example, if message numbers 1 and 3 have arrived but not 2, then message 1 can be delivered, but message 3 delivery must wait for message 2 to arrive and be delivered.

RM Sequence Profiles

A sequence has a profile whose properties describe how the sequence behaves. The profile properties can be set in a service's policy file. The profiles properties common to input and output sequences are:

  • Expires: If this absolute time has expired since the sequence was opened, then the sequence status is set to Faulted.
  • ExpiresSeconds: If this time interval has expired since the sequence was opened, then the sequence status is set to Faulted. Expires overrides ExpiresSeconds.
  • Delete: If this absolute time has expired since the sequence was opened, then the sequence is deleted.
  • DeleteSeconds: If this time interval has expired since the sequence was opened, then the sequence is deleted. Delete overrides DeleteSeconds.
  • Priority: The processing priority of the sequence. This is used to decide which messages to deliver and transmit first.
  • MaxFaultHandlerAttempts: If a fault has been pushed on an application fault handler this many times, it will not be pushed again.

The profile properties unique to input sequences are:

  • InOrder: Whether the messages should be delivered in the same order they were sent.
  • AutomaticallyAck: Whether the messages should be automatically acked by separate messages as each input message arrives. Without this, the destination application code must call an API to direct an ack to be sent, which can reduce the number of ack messages at the expense of more complex application code.
  • MaxDeliverAttempts: If a message delivery has aborted this many times, no more deliver attempts will be made and the sequence is put in the Faulted state with the fault reason being PoisonMessage.

The profile properties unique to output sequences are:

  • RetransmissionIntervalSeconds: If the retransmission wait time has expired since a message transmission attempt without an ack, the message should be retransmitted.
  • RetransmissionExponentialBackoff: Whether exponential backoff should be used for message retransmissions, so that each successive transmit attempt waits twice as long as the previous one.
  • MessageExpiresIntervalSeconds: If this interval has been exceeded since a message was sent, then transmit attempts should stop and the sequence should be put in the Faulted state.
  • MaxTransmitAttempts: After this number of transmit attempts, no more attempts will be made.
  • AutomaticallySendTerminateSequence: Whether the TerminateSequence message should be automatically sent as soon as the full sequence range has been acked.
  • CreateSequenceTimeoutSeconds: When a CreateSequence is sent, this is the amount of time to wait for a CreateSequenceResponse before giving up.
  • AddAckRequestedHeaderToMessages: True indicates that an AckRequested header should be added to each reliable message.

A duplex sequence has no profile of its own. Instead, its input and output sequences each have a profile.

Profile properties are immutable after the sequence has been created.

RM Sequence Groups

A sequence group is a set of sequences that are used together. As an example, take a seller service that supports purchases. Each individual purchase may have a duplex sequence with a buyer and a duplex sequence with a shipper. The following input messages trigger work for the seller service:

  • The arrival of a message from the buyer would cause the delivery of the sequence and message to the service application code. To process this message, the service may need to send messages to the buyer and the shipper.
  • The arrival of a message from the shipper would cause the delivery of the sequence and message to the service application code. To process this message, the service may need to send messages to the buyer and the shipper.

One purpose of a sequence group is to prevent deadlocks by using a larger granularity for locking—effectively locking all the sequences in the group together. Let's use the above example to illustrate:

  • Without a sequence group: The thread handling the buyer input message will lock the buyer sequence and try to lock the shipper sequence. The thread handling the seller input message will lock the seller sequence and try to lock the buyer sequence. If the two input messages have unfortunate timing, a deadlock will occur.
  • With a sequence group: When either of the two input messages arrives, the two threads will try to get the single sequence group lock. One will win and finish processing. Then the other will get the single lock and finish processing. A deadlock is not possible when there is only a single exclusive lock.

A second purpose of a sequence group is to reduce the number of mappings that an application has to maintain. The application can set the sequence group ID to be the service instance ID (for example, a purchase ID) that is the primary key to the service instance state. When a message and its sequence are delivered to an application, the group ID is available from the sequence, so the application immediately has the service instance ID. Without the group ID, the application would have to maintain a mapping of sequence IDs to service instance IDs.

A third purpose of a sequence group applies to queued sequences. It allows optimistic activation of all the sequences that may be used together with a single database access. This optimization is not yet implemented in WseRM.

A sequence group is only an ID. The only object involved is a string. It is a property on a sequence called GroupID.

A group ID typically identifies a service instance (for example, a particular purchase ID), which may be a database key for the service instance state. This allows the application to go from the current input sequence to the related service instance state. This is described in more detail in WseRMProgramming.doc.

RM Sequence Managers

Sequence managers are central to the WseRM programming model. They enable you to establish and manage the RM sequence. A sequence manager can be configured to be just one-way (input or output) or duplex (input and output). A sequence manager is a factory for sequence created by clients. A one-way sequence manager manages all sequences with the same local endpoint:

  • An input sequence manager's local endpoint is typically published so remote services can initiate sequences.
  • An output sequence manager's local endpoint is used for WS-RM ack and fault messages. It is provided to the remote endpoints during the CreateSequence/Response exchange.

Each sequence manager has an ID. These IDs are used by an application to get the sequence manager at runtime.

A sequence has only one sequence manager. Sequence managers have the same type categories as sequences. There are input, output, and duplex sequence managers. There are direct and queued sequence managers. The following diagram illustrates the relationships between duplex vs. input vs. output sequences and sequence managers.

  • A duplex sequence manager has 1 input sequence manager and 1 output sequence manager.
  • A duplex sequence has 1 input sequence and 1 output sequence.
  • An Xxx sequence manager has multiple Xxx sequences (Xxx is Input, Output, or Duplex).

Other constraints not shown in the diagram are:

  • A duplex sequence manager and its input and output sequence managers share the same manager ID.
  • A duplex sequence and its input and output sequences share the same sequence ID.
  • A duplex sequence manager and its input and output sequence managers are from the same implementation (for example, direct vs. queued).
  • A sequence manager and the sequences that it manages are from the same implementation (for example, direct vs. queued).
  • Since a sequence group is a grouping of all sequences that are used together, a natural constraint is that a duplex sequence and its input and output sequences all share the same sequence group. Of course, the same group can also contain other sequences.
  • A sequence group can contain sequences from different implementations. However, when a sequence group mixes different implementations, the goals of the sequence group (that is, deadlock prevention and optimistic caching) are compromised. The WseRM implementation of direct and queued does deadlock prevention across these two implementations.

A sequence manager listens on the wire for messages arriving at its endpoint:

  • For an input sequence manager, a CreateSequence message arriving at its endpoint will cause it to create a new input sequence. For a duplex sequence manager, a CreateSequence message arriving at its input sequence's endpoint will cause it to create a new duplex sequence. A creation request is refused if what the request message asks for (input vs. duplex) does not match the targeted endpoint's sequence manager (input vs. duplex).
  • For a reliable message arriving at an input sequence manager's endpoint, the sequence manager knows which sequence the message belongs to by its WS-RM Sequence header's Identifier element.
  • For RM infrastructure messages (for example, SequenceAcknowledgement, TerminateSequence, sequence Fault) arriving at a sequence manager's endpoint, the sequence manager knows which sequence the message belongs to by the appropriate WS-RM header's Identifier element.

A sequence manager has a default sequence profile that is used whenever the sequence-specific profile is not provided. A sequence-specific profile can optionally be provided by the application prior to creation that overrides the manager-level profile settings in the policy file.

A Taste of Programming

To give you a taste of the WseRM programming model, this section provides some example code for a simple one-way queued sequence using WseRM.

In this example, the client creates a one-way output sequence, sends 3 messages, and closes the sequence. The sequence manager is defined in the WseRM assertion in the WSE policy file:

<policy name="ClientPolicyForServers">
<WseRM ManagerID="ManagerForServers"
        DatabaseConnectionString="SERVER=localhost;Integrated Security=SSPI;DATABASE=RMClientDB">
      <OutputManager>
<LocalEndpoint Address="soap.tcp://localhost:2001/PingClient/AcksTo"/>
</OutputManager>
   </WseRM>
</policy>

There is only one output sequence manager. The DatabaseConnectionString indicates that it is a queued sequence manager that stores its RM metadata in the database named "RMClientDB". The local endpoint address is where the client receives acks and remotely generated sequence faults.

The client must bind the proxy class with the policy using a Policy attribute with the same policy name as in the policy file:

[Policy("ClientPolicyForServers")]
public partial class HandlerForSequencesWithClients : Microsoft.Web.Services3.WebServicesClientProtocol { }

Proxy classes are generated as partial classes, which makes it convenient to add this attribute in a way that is not lost if the proxy is regenerated.

The client does the following:

WseRM.IOutputSequenceManager managerForServers = WseRM.OutputSequenceManagers.Get("ManagerForServers");
using (System.Transactions.TransactionScope scope 
= new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.RequiresNew))
{
   Uri serverUri = new Uri("soap.tcp://localhost:2002/FooServer/ClientInput");
   EndpointReference serverEpr = new EndpointReference(serverUri);
WseRM.IOutputSequence outputSequence = managerForServers.CreateSequence(serverEpr,...); 
HandlerForSequencesWithClients serverProxy = new HandlerForSequencesWithClients();
outputSequence.SetProxy(serverProxy);
   serverProxy.FooHandler("TextForMessage1");
serverProxy.FooHandler("TextForMessage2");
   serverProxy.FooHandler("TextForMessage3");
   outputSequence.Close();   // this causes the LastMessage to be sent
   scope.Complete();       // this allows the transaction to commit
}

The client gets the sequence manager from a static collection OutputSequenceManagers using the sequence manager name in the policy file. This collection was populated when the policy assertion was parsed by the WseRM policy assertion code. The client uses the manager to create an output sequence to the server. Then it creates a proxy and binds it to the sequence using the SetProxy method. It then sends 3 messages and closes the sequence. This is all done in a single transaction using the .NET Framework System.Transactions programming model. The messages are not in the output queue until the transaction commits. After commit, the RM source can start transmitting messages.

The server has a single policy in the policy file describing an input sequence manager:

<policy name="ServerPolicyForClients">
   <WseRM ManagerID="ManagerForClients" 
      DatabaseConnectionString="SERVER=localhost;Integrated Security=SSPI;DATABASE=RMServerDB">
      <InputManager>
         <LocalEndpoint Address="soap.tcp://localhost:2002/FooServer/ClientInput"/>
      </InputManager>
   </WseRM>
</policy>

The server input manager must have an addressable endpoint.

The server is a WebService with a WebMethod to handle the input messages from all clients:

[Policy("ServerPolicyForClients")]
public class HandlerForSequencesWithClients : System.WebServices.WebService
{
   [WebMethod()]
[SoapDocumentMethod(Action = "urn:FooMessageAction")]
   public void FooHandler(string foo)
   {
      WseRM.IInputSequence inputSequence = WseRM.InputSequence.Current;
Console.WriteLine("SequenceID " + inputSequence.ID + ": Delivered a 
message with Text = " + foo);
}
}

This class is bound to the policy using a Policy attribute with the same policy name as in the policy file. This method is already within a transaction scope when it is called—the same transaction that dequeued the message. If the transaction aborts, not only are any application state changes rolled back, but the message is back on the input queue and will be tried again later—perhaps by another server farm node. The FooHandler method can also get the input sequence from the thread static InputSequence.Current property, which is useful for things like examining the status of the sequence.

RM Implementations

The reference implementation provides generic sequence and sequence manager classes that call RM store interfaces. These classes contain all WSE messaging glue. The RM store interfaces are high-level enough to allow storage-specific algorithms to be included to allow storage-specific performance optimizations.

There is one direct RM implementation and one queued RM implementation provided in WseRM. They use hash tables and SQL Server, respectively, in the RM store implementation.

More details are provided in the following documentation in the WseRM install:

...\samples\CS\RM\docs\WseRMImplementation.doc

Systems Management

A goal of the queued RM implementation was to simplify systems management by not requiring the introduction of additional queuing servers or databases. Instead, the WseRM queued tables and stored procedures can be added to the application's existing database.

The database containing the RM tables is owned by the application service administrator. To facilitate systems management, the schemas of the RM tables are published in the following document:

...\samples\CS\RM\docs\WseRMImplementation.doc

and script file:

...\samples\CS\RM\runtime\GenerateDB.sql

This allows an unlimited variety of queries to be written for management and analysis. Stored procedures are provided to enforce expire and delete times, which can be called periodically from SQL Server Agent.

Conclusion

This article has provided only an overview of an implementation of WS-ReliableMessaging for WSE 3.0. The next steps are to go through the RM examples and use WseRM to gain an understanding of how WS-RM can provide reliable delivery in your applications. To begin, install WseRM and see the WseRMSamples.doc paper in the docs directory.

Feedback is welcome and can be sent to wsefeed@microsoft.com. Thanks.

Show: