blogs.conchango.com

welcome to the conchango blogging site
Welcome to blogs.conchango.com Sign in | Join | Help
in Search

Simon Evans' Blog

My blog covers the technology areas I focus on here at Conchango, namely Architecture using the .Net Framework, ASP.net 2.0, WCF and Agile development practices.

A comprehensive guide to using MsmqIntegrationBinding with MSMQ 3.0 in WCF

Overview of WCF architecture

Figure 1 illustrates the generic architecture WCF employs for all services.

Figure 1 - General WCF Architecture

Figure 1 - General WCF Architecture

The outer service represents the AppDomain of the service. A WCF service consists of contracts (data, fault and service contracts), a service instance (implementation) which is the implementation of a service contract, and configuration which contains the service endpoint information used at runtime to create the channel stack and the service instance.

A WCF service endpoint consists of two core elements: the service contract and a binding. A binding describes how the service will communicate with clients at runtime. WCF provides several default bindings out of the box, covering many common communication scenarios such as BasicHttpBinding and MsmqIntegrationBinding. In addition to creating bindings from scratch, you can also extend and customize the default bindings to work as you wish.

Service Instancing and opening communication

Figure 1 illustrates what happens when a WCF service is run. Ultimately, the aim of the service is to map messages from clients to operations within the service instance. The service instance requires a runtime host and the host must be opened for communication to occur. The ServiceHost<T> class represents this host in code. When the host's constructor is run, it uses the service endpoint configuration combined with the contracts defined in assemblies (retrieved using reflection) to create a ServiceDescription. Thus, the Service host description contains the complete description of the service, including what contracts to use, what service implementation to use for the service contract and how the service will communicate with the outside world (binding).

When the ServiceHost<T>.Open() method is run the Service Description is used to create a Channel Stack and a Service Instance. The Channel Stack is the runtime equivalent of the Binding information. The stack contains all the protocols required in order for the communication. As a minimum for any binding, this must contain at least a Transport and an Encoding. Additional protocols (such as Sessions and Transactions) can then be applied on top. For more information on the channel stack architecture, look at the following MSDN article: http://msdn2.microsoft.com/en-us/library/ms729840.aspx.

Once the service host is open, the service is ready to receive incoming requests from a client. The inbound message gets received through the channel stack and the Dispatcher is then responsible for assigning the message to the corresponding Service Instance operation, deserializing the message using the appropriate deserializer and executing the operation on the service instance. For more information on serialization , look at the following blog entry: http://blogs.conchango.com/simonevans/archive/2007/05/14/WCF-serialization-with-MSMQ.aspx.

Behaviors in WCF

Behaviors are a way of changing the default way in which either a service instance, or operation will execute when run. Behaviors either inherit from interface IServiceBehavior or IOperationBehavior. They are attached to the service description when the host is constructed, using a mixture of configuration, and/ or any additional code you choose to add. Whether you add a behavior in code or in configuration depends on whether you want the behavior to be configurable, or if you need to behavior to be permanently applied.

Examining runtime metadata using InstanceContext and OperationContext

Whilst the service instance is running, the InstanceContext class provides you with the ability to examine metadata about the instance at runtime, such as information about the channel stack and the host. The lifetime of the InstanceContext is aligned to the life time of the service instance. Likewise, the OperationContext provides you with runtime access to service operation metadata for the lifetime of the call to the service operation. OperationContext provides you with access to information such as message headers and properties for inbound and outbound messages, and the Dispatcher.

Handling Concurrency

InstanceContextMode and ConcurrencyMode are service behavior attributes applied to the Service Description when the host is constructed. Together, these two attributes are used to configure how a service will control the lifetime of service instances and how many messages a dispatcher will hand over to an instance.

The InstanceContextMode controls the lifetime of a service instance. By default, this is set to PerSession. This means the service instance will last for the lifetime of the corresponding Session (which equals PerCall if no session is available). PerCall instancing means the instance will be destroyed after the service operation completes. The final option here is Single, which means that the instance context will only be destroyed when the host is closed.

The ConcurrencyMode handles how many messages the dispatcher will hand over to the service instance. The default is Single, which means the service instance runs single threaded, but this can be changed to run multi threaded by using the setting Multi.

Additional to InstanceContextMode and ConcurrencyMode, the ServiceThrottlingBehavior controls how many concurrent service instances will be run by the host at one time (the MaxConcurrentInstances property), and how many concurrent calls can be made to a single instance (the MaxConcurrentCalls property). It is worth noting with this final property that the channel stack will automatically ready the next inbound message even when concurrent calls are set to 1.

All of these behaviors together control how concurrency and threading will be handled within a WCF service. Figures 2 to 4 below show how these variables can affect threading and concurrency in WCF.

Figure 2 

Figure 2 – PerCall instancing, Single Threaded with 2 concurrent instances

Figure 2 shows a service that allows two concurrent service instances to run, each of which is destroyed when the call to the operation is complete (PerCall). Each service instance is configured to only allow one thread to run at a time. This means that of the three inbound messages (shown on the left), the dispatcher will handle two currently using two service instances in the process. The third message will be made ready by the channel stack for handling by the dispatcher.

Figure 3

Figure 3 – PerCall instancing, Single Threaded with 1 concurrent instance

Figure 3 shows what happens when the concurrent instances property is set to 1. The result is only one service instance is created and runs single threaded. A second message is made ready for the dispatcher in the channel stack and is not processed until the service instance is destroyed at the end of the first operation call.

Figure 4

Figure 4 - Single Instance, Multi threaded with 2 concurrent calls

Figure 4 shows how using multi threaded concurrency with a single instance would affect how the service behaves with two concurrent calls allowed. The top two inbound messages from the channel stack would make a call to an operation on a single service instance concurrently. This service instance would not be destroyed at the end of the call to the operation. The third message would be waiting in the channel stack, and as soon as the service instance made a thread free for use, the dispatcher will call the operation for the third message against the same service instance.

Service Exceptions

When a service instance throws an unhandled exception, the InstanceContext is said to be in a Faulted state, and the Faulted event will be raised. This event can be handled in the service host. Once the host is in a faulted state, no further service instances will be created until the channel stack is taken out of a faulted state. This can be achieved by closing and reopening the host (services hosted in IIS automatically handled restarting the host).

Using MSMQ with WCF

Why use MSMQ for communication?

MSMQ provides core features which can be employed to solve various architectural problems. It is a store and forward mechanism for messages, and provides core features such as durability, exactly once delivery and transactional support. MSMQ should be employed in situations where you need a buffer in communication between two endpoints, such as cases where a service intermediary is required to deal with service (un)availability and reliability.

Before looking at using MSMQ with WCF, it is important to understand some core concepts of MSMQ, particularly when it is employed in a distributed architecture, where endpoints are running on different servers. These concepts are largely abstracted away from you writing service code, but their impact is important to understand.

Firstly, the term store and forward refers to how MSMQ will send a message across machine boundaries. When a message is sent from one machine to another, MSMQ will send the message to an outbound local queue and then forward it onto the remote queue on the other machine. If the remote machine is offline, the message will remain on the local queue for a period of time until the remote queue becomes available, or the attempt to process the message times out. The store and forward mechanism deals with the requirement that the remote machine may be offline when the message is sent. If the timeout for processing the message from outbound local queue expires, the message will be removed from the local queue and can be optionally moved to a dead letter queue. This queue is the final resting place for a message ensuring that the message has not been lost.

The second concept to understand is durability. During the store and forward mechanism a message can optionally be persisted to disk. The reason for doing this would be to recover from machine failures and reboots on either the outbound queue or the remote queue.

The next concept is exactly once delivery. This ensures that an individual message is assured to be sent exactly once. This assurance should be used when it is important not to receive duplicates of the same message.

Finally, MSMQ can optionally support transactions either homogeneously using a local MSMQ transaction manager, or heterogeneously using DTC. When transactions are required, MSMQ requires that the message uses exact once delivery with durability in order to achieve the requirements of an ACIDic transaction.

It is worth also understanding the transport that MSMQ uses for sending and receiving messages. Messages are sent using TCP/IP and received using RPC. As RPC is a heavy weight protocol, it is seen as best practice to send to a remote machine queue, but receive from a local queue. Indeed remote receives on a transactional queue are not supported in MSMQ 3.0, because of the performance implications of doing so.

MsmqIntegrationBinding in Detail

MsmqIntegrationBinding is an out of the box binding for communication with MSMQ alongside NetMsmqBinding. The reason that WCF provides two bindings to communicate with MSMQ is that NetMsmqBinding will only work in pure WCF only architectures, where all clients and services are WCF based. It does however provide a much richer channel stack, including support for Sessions, which in turn gives you greater transactional control.

If you are working in a heterogeneous architecture where WCF is interoperating with other MSMQ based clients and services, MsmqIntegrationBinding is the only option available to you. MsmqIntegrationBinding is limited in the communication policies it can use, and it also requires service contracts specifically designed for use with the binding. Figure 5 below shows the WCF architecture from the point of view of MsmqIntegrationBinding.

Figure 5

Figure 5 - WCF architecture using MsmqIntegrationBinding

MsmqIntegrationBinding requires contracts that are designed only for its use. Unless additional behaviors are applied to the dispatcher, the service contract can contain only one service operation. For more information on extending the service contract in this way, look at the following link: http://msdn2.microsoft.com/en-us/library/ms752265.aspx. Service operations for MsmqIntegrationBinding can contain only one argument typed as MsmqMessage<T>, where T is the data contract used in the MSMQ message body.

MsmqIntegrationBinding is also limited in terms of what policies can be applied in the channel stack. The Transport used is TCPIP for sending messages and RPC for receiving messages, and it is only at the transport level can security be applied (there is no message level security available).

Beyond Transport and Encoding policies, the only optional addition to the channel stack is transactional support. For MsmqIntegrationBinding, transactions have to use auto completing transactions. This is achieved by applying an operation behavior to the service implementation with attributes TransactionScopeRequired and TransactionAutoComplete set to true. This means that the transaction (with MSMQ) will automatically be committed if the service operation completes without an unhandled exception. This is limited transactional support because it means your transactions cannot flow beyond the service operation you are running into other service operations or behaviors. The reason MsmqIntegrationBinding does not support explicit transaction control is because explicit transactions in WCF require Sessions to be enabled, which are not supported by this binding.

MSMQ comes in various different versions. MsmqIntegrationBinding supports both version 3.0 and 4.0. Version 3.0 is provided with Windows XP and Windows Server 2003. MSMQ v4.0 is provided with Windows Vista and Windows Server 2008; it provides richer functionality for custom dead letter queues and poison messaging (see later).

The following table lists key properties exposed by the MsmqIntegrationBinding, how these are used and what versions of MSMQ will support this property:

Property name

What does it do?

MSMQ version

CustomDeadLetterQueue

Specifies the name of the custom dead letter queue to use.

4.0 only

DeadLetterQueue

Specifies whether to use a system, custom or no dead letter queue.

All (3.0 does not support custom dead letter queues).

Durable

Specifies whether MSMQ will persist the message to disk, making it durable to server shutdown.

All

ExactlyOnce

Specifies whether MSMQ will ensure that a message is delivered exactly once.

All

ReceiveRetryCount

Specifies the number of times the channel will retry attempting to process the message from the queue (executing the service operation). This is used for poison message handling (see below).

All

ReceiveErrorHandling

Specifies how the channel will respond to a message which fails to be processed once all retry cycles are exhausted. This is used for poison message handling (see below).

All (3.0 only supports Drop and Fault values).

MaxRetryCycles

Specifies how many retry cycles are used in an MSMQ 4.0 retry queue.

4.0 only

RetryCycleDelay

Specifies a delay between retry cycles used by the MSMQ 4.0 retry queue.

4.0 only

Message processing failure

When an MSMQ message is processed by the channel stack and handed to the service instance from the dispatcher, the service operation must cope with exceptions. These exceptions can be separated into two distinct groups: expected exceptions and unexpected exceptions. For unexpected exceptions, WCF provides a built in mechanism called Poison Message Handling, which is covered later in this document.

Expected Exceptions

A general principle of all message based architectures is to handle the message as quickly as possible to a final destination (normally your endpoint). This is important to remember when dealing with expected exceptions. These are exceptions raised within the service operation that are expected to happen under certain known conditions. Expected exceptions are best dealt with explicitly in code, with a definitive outcome for handling the exception. For example, a message may contain data which is invalid for the service implementation's business rules. In this example, the message would never be allowed into the endpoint, so the message needs to be routed to another destination, such as another queue.

The best way to handle expected exceptions is to catch the typed exception in a try catch block, as place the action for the exception in the catch block. With MsmqIntegrationBinding it is important to remember that transactional messages can only be processed using automatic transactions, which means that your handling code needs to participate in the outer transaction if you want the message to be moved off of the queue. The metadata about the MSMQ message can be retrieved using the OperationContext.IncomingMessageProperties collection, which for MsmqIntegrationBinding are typed as MsmqIntegrationMessageProperty. The following code is an example of such a handler for an expected exception used within a service operation that executes in an automatic transaction:

try

{

// do service operation logic here:

}

catch (MyValidationException)

{

MsmqIntegrationMessageProperty integrationMessageProperty = operationContext.IncomingMessageProperties[MsmqIntegrationMessageProperty.Name] as MsmqIntegrationMessageProperty;

string label = integrationMessageProperty.Label;

if (integrationMessageProperty.Body != null)

{

using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))

{

using (MessageQueue badQueue = new MessageQueue(badQueuePath))

{

badQueue.Send(integrationMessageProperty.Body, label, MessageQueueTransactionType.Automatic);

scope.Complete();

}

}

}

}

Poison Message handling for Unexpected Exceptions in MSMQ 3.0

MsmqIntegrationBinding includes a built in mechanism for dealing with unexpected exceptions called Poison Message Handling. MSDN includes a lot of useful information about poison messaging at http://msdn2.microsoft.com/en-us/library/ms789028.aspx. More specifically, there is also information about using Poison Messaging with MSMQ 3.0 at http://msdn2.microsoft.com/en-us/library/ms751472.aspx. There are however quite a few missing details from these articles.

MsmqIntegrationBinding exposes properties called ReceiveErrorHandling and ReceiveRetryCount which deal with the number of times the channel stack will attempt to process the message from the queue and what happens when this retry number is exceeded. In MSMQ 3.0, the only options for dealing with poison messages is either to Drop the message (remove it from the queue and ignore) or Fault, which will cause the service instance to be put in a Faulted state. When Fault is chosen for error handling, WCF will throw an MsmqPoisonMessageException, which can be handled by writing a custom error handler which implements IErrorHandler. This error handler can be attached to the service description (either in code or configuration) by attaching the error handler to a behavior. The above MSDN articles provide code samples for this scenario.

Any error handler that is attached to the service description is executed when any exception occurs within the channel stack. The HandleError method is called, passing in the exception that was thrown. If the error is handled by the error handler, the method returns true back to the channel stack and no other error handler is called. The standard scenario for a handler for poison message exceptions is to move the message off of the main queue to allow other messages to be processed, and reopen the service host so that communication can continue.

It is important to understand the context in which the MsmqPoisonMessageException is thrown. When this exception is thrown, receive retries have been exhausted and the InstanceContext is faulted. This means that communication is stopped in the channel stack and the host is no longer open for communication. The message transaction will therefore rollback and return to the message to the queue. The poison message handler is then able to receive this message from the queue to move it onto another queue so that normal message processing can resume once communication is reopened.

Using a poison message handler needs to be understood from the point of view of concurrency. As the original transaction is rolled back a new transaction is created to move the message from the queue, there is a possibility that other concurrent instance will attempt to process the message off of the queue before the host has been completely closed and the poison handler is run. Therefore, the poison message mechanism cannot guarantee how many times the service operation will be executed before the handler is run. The handler is running under transactional race conditions where other processing service instances might not yet have fully rolled back their transactions. Therefore the handler needs to attempt to receive the message off the queue several times to ensure the message is moved off the queue.

In MSMQ 3.0, the ReceiveRetryCount for each message processed concurrently is maintained in memory in the channel stack and can only be maintained for a maximum of 256 concurrent messages. The current count is available from the MsmqIntegrationMessageProperty.AbortCount property of the inbound message in the InstanceContext. Because this maintained in memory, an MsmqPoisonMessageException will cause all concurrent instances to be destroyed as the host is closed, and there all retry counts are reset. This means that with concurrent instances, it is feasible that messages may be processed more times than their retry count, because the count is reset during processing. This problem is resolved in MSMQ 4.0, where MSMQ stores the retry count on disk outside of the channel stack.

Related articles

Published 17 September 2007 13:52 by simon.evans

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

resume » A comprehensive guide to using MsmqIntegrationBinding with MSMQ … said:

October 15, 2007 03:43
 

http://blogs.conchango.com/simonevans/archive/2007/09/17/A-comprehensive-guide-to-using-MsmqIntegrationBinding-with-MSMQ-3.0-in-WCF.aspx#comments said:

April 6, 2008 16:07
 

stan said:

in msmq 3.0, can a dlq receiving wcf service endpoint be written so that only DLQ messages from a specific app are played back to the service?  Would such messages be automatically purged after successful playback?

in 3.0, can poisoned messages be written to the DLQ in the method coded for handling the fault?  

June 5, 2008 17:15

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server (Personal Edition), by Telligent Systems