WEBSITE

Programming WCF : Queued Services - Queued Calls

10/12/2010 3:19:00 PM
WCF provides support for queued calls using the NetMsmqBinding. With this binding, instead of transporting the messages over TCP, HTTP, or IPC, WCF transports the messages over MSMQ. WCF packages each SOAP message into an MSMQ message and posts it to a designated queue. Note that there is no direct mapping of WCF messages to MSMQ messages, just like there is no direct mapping of WCF messages to TCP packets. A single MSMQ message can contain multiple WCF messages, or just a single one, according to the contract session mode (as discussed at length later). In effect, instead of sending the WCF message to a live service, the client posts the message to an MSMQ queue. All that the client sees and interacts with is the queue, not a service endpoint. As a result, the calls are inherently asynchronous (because they will execute later, when the service processes the messages) and disconnected (because the service or client may interact with local queues).

1. Queued Calls Architecture

As with every WCF service, in the case of a queued service the client interacts with a proxy, as shown in Figure 1.

Figure 1. Queued calls architecture


However, since the proxy is configured to use the MSMQ binding, it does not send the WCF message to any particular service. Instead, it converts the call (or calls) to an MSMQ message (or messages) and posts it to the queue specified in the endpoint’s address. On the service side, when a service host with a queued endpoint is launched, the host installs a queue listener, which is similar conceptually to the listener associated with a port when using TCP or HTTP. The queue’s listener detects that there is a message in the queue, de-queues the message, and then creates the host side’s chain of interceptors, ending with a dispatcher. The dispatcher calls the service instance as usual. If multiple messages are posted to the queue, the listener can create new instances as fast as the messages come off the queue, resulting in asynchronous, disconnected, and concurrent calls.

If the host is offline, messages destined for the service will simply remain pending in the queue. The next time the host is connected, the messages will be played to the service. Obviously, if both the client and the host are alive and running and are connected, the host will process the calls immediately.

2. Queued Contracts

A potentially disconnected call made against a queue cannot possibly return any values, because no service logic is invoked at the time the message is dispatched to the queue. Not only that, but the call may be dispatched to the service and processed after the client application has shut down, when there is no client available to process the returned values. In much the same way, the call cannot return to the client any service-side exceptions, and there may not be a client around to catch and handle the exceptions anyway. In fact, WCF disallows using fault contracts on queued operations. Since the client cannot be blocked by invoking the operation—or rather, the client is blocked, but only for the briefest moment it takes to queue up the message—the queued calls are inherently asynchronous from the client’s perspective. All of these are the classic characteristics of one-way calls. Consequently, any contract exposed by an endpoint that uses the NetMsmqBinding can have only one-way operations, and WCF verifies this at service (and proxy) load time:

//Only one-way calls allowed on queued contracts
[ServiceContract]
interface IMyContract
{
[OperationContract(IsOneWay = true)]
void MyMethod();
}

Because the interaction with MSMQ is encapsulated in the binding, there is nothing in the service or client invocation code pertaining to the fact that the calls are queued. The queued service and client code look like any other WCF service and client code, as shown in Example 1.

Example 1. Implementing and consuming a queued service
//////////////////////// Service Side ///////////////////////////
[ServiceContract]
interface IMyContract
{
[OperationContract(IsOneWay = true)]
void MyMethod();
}
class MyService : IMyContract
{
public void MyMethod()
{...}
}
//////////////////////// Client Side ///////////////////////////
MyContractClient proxy = new MyContractClient();
proxy.MyMethod();
proxy.Close();

3. Configuration and Setup

When you define an endpoint for a queued service, the endpoint address must contain the queue’s name and designation (that is, the type of the queue). MSMQ defines two types of queues: public and private. Public queues require an MSMQ domain controller installation or Active Directory integration and can be accessed across machine boundaries. Applications in production often require public queues due to the secure and disconnected nature of such queues. Private queues are local to the machine on which they reside and do not require a domain controller. Such a deployment of MSMQ is called a workgroup installation. During development, and for private queues they set up and administer, developers usually resort to a workgroup installation.

You designate the queue type (private or public) as part of the queued endpoint address:

<endpoint
address = "net.msmq://localhost/private/MyServiceQueue"
binding = "netMsmqBinding"
...
/>

In the case of a public queue, you can omit the public designator and have WCF infer the queue type. With private queues, you must include the designator. Also note that there is no $ sign in the queue’s type.

3.1. Workgroup installation and security

When you’re using private queues in a workgroup installation, you typically disable MSMQ security on the client and service sides.  Briefly, the default MSMQ security configuration expects users to present certificates for authentication, and MSMQ certificate-based security requires an MSMQ domain controller. Alternatively, selecting Windows security for transport security over MSMQ requires Active Directory integration, which is not possible with an MSMQ workgroup installation. For now, Example 2 shows how to disable MSMQ security.

Example 2. Disabling MSMQ security
<system.serviceModel>
...
<endpoint
address = "net.msmq://localhost/private/MyServiceQueue"
binding = "netMsmqBinding"
bindingConfiguration = "NoMSMQSecurity"
contract = "..."
/>
...
<bindings>
<netMsmqBinding>
<binding name = "NoMSMQSecurity">
<security mode = "None"/>
</binding>
</netMsmqBinding>
</bindings>
</system.serviceModel>


Note: If you must for some reason enable security for development in a workgroup installation, you can configure the service to use message security with username credentials.
3.2. Creating the queue

On both the service and the client side, the queue must exist before client calls are queued up against it. There are several options for creating the queue. The administrator (or the developer, during development) can use the MSMQ control panel applet to create the queue, but that is a manual step that should be automated. The host process can use the API of System.Messaging to verify that the queue exists before opening the host. The class MessageQueue offers the Exists() method for verifying that a queue is created, and the Create() methods for creating a queue:

public class MessageQueue : ...
{
public static MessageQueue Create(string path); //Nontransactional
public static MessageQueue Create(string path,bool transactional);
public static bool Exists(string path);
public void Purge();
//More members
}

If the queue is not present, the host process can first create it and then proceed to open the host. Example 3 demonstrates this sequence.

Example 3. Verifying a queue on the host
ServiceHost host = new ServiceHost(typeof(MyService));

if(MessageQueue.Exists(@".\private$\MyServiceQueue") == false)
{
MessageQueue.Create(@".\private$\MyServiceQueue",true);
}
host.Open();

In this example, the host verifies against the MSMQ installation on its own machine that the queue is present before opening the host. If it needs to, the hosting code creates the queue. Note the use of the true value for the transactional queue, as discussed later. Note also the use of the $ sign in the queue designation.

The obvious problem with Example 9-3 is that it hard-codes the queue name, not once, but twice. It is preferable to read the queue name from the application config file by storing it in an application setting, although there are problems even with that approach. First, you have to constantly synchronize the queue name in the application settings and in the endpoint’s address. Second, you still have to repeat this code every time you host a queued service. Fortunately, it is possible to encapsulate and automate the code in Example 3 in my ServiceHost<T>, as shown in Example 4.

Example 4. Creating the queue in ServiceHost<T>
public class ServiceHost<T> : ServiceHost
{
protected override void OnOpening()
{
foreach(ServiceEndpoint endpoint in Description.Endpoints)
{
endpoint.VerifyQueue();
}
base.OnOpening();
}
//More members
}
public static class QueuedServiceHelper
{
public static void VerifyQueue(this ServiceEndpoint endpoint)
{
if(endpoint.Binding is NetMsmqBinding)
{
string queue = GetQueueFromUri(endpoint.Address.Uri);
if(MessageQueue.Exists(queue) == false)
{
MessageQueue.Create(queue,true);
}
}
}
//Parses the queue name out of the address
static string GetQueueFromUri(Uri uri)
{...}
}


In Example 4, ServiceHost<T> overrides the OnOpening() method of its base class. This method is called before opening the host, but after calling the Open()ServiceHost<T> iterates over the collection of configured endpoints. For each endpoint, if the binding used is NetMsmqBinding—that is, if queued calls are expected—ServiceHost<T> calls the extension method VerifyQueue() of the ServiceEndpoint type and asks it to verify the presence of the queue. The static extension VerifyQueue() method of QueuedServiceHelper parses the queue’s name out of the endpoint’s address and uses code similar to that in Example 3 to create the queue if needed. method.

Using ServiceHost<T>, Example 9-3 is reduced to:

ServiceHost<MyService> host = new ServiceHost<MyService>();
host.Open();

The client must also verify that the queue exists before dispatching calls to it. Example 5 shows the required steps on the client side.

Example 5. Verifying the queue by the client
if(MessageQueue.Exists(@".\private$\MyServiceQueue") == false)
{
MessageQueue.Create(@".\private$\MyServiceQueue",true);
}
MyContractClient proxy = new MyContractClient();
proxy.MyMethod();
proxy.Close();

Again, you should not hardcode the queue name and should instead read the queue name from the application config file by storing it in an application setting. And again, you will face the challenges of keeping the queue name synchronized in the application settings and in the endpoint’s address, and of writing queue verification logic everywhere your clients use the queued service. You can use QueuedServiceHelper directly on the endpoint behind the proxy, but that forces you to create the proxy (or a ServiceEndpoint instance) just to verify the queue. You can, however, extend my QueuedServiceHelper to streamline and support client-side queue verification, as shown in Example 6.

Example-6. Extending QueuedServiceHelper to verify the queue on the client side
public static class QueuedServiceHelper
{
public static void VerifyQueues()
{
Configuration config = ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
ServiceModelSectionGroup sectionGroup =
ServiceModelSectionGroup.GetSectionGroup(config);

foreach(ChannelEndpointElement endpointElement in
sectionGroup.Client.Endpoints)
{
if(endpointElement.Binding == "netMsmqBinding")
{
string queue = GetQueueFromUri(endpointElement.Address);

if(MessageQueue.Exists(queue) == false)
{
MessageQueue.Create(queue,true);
}
}
}
}
//More members
}


Example 6 uses the type-safe programming model that is offered by the ConfigurationManager class to parse a configuration file. It loads the WCF section (the ServiceModelSectionGroup) and iterates over all the endpoints defined in the client config file. For each endpoint that is configured with the MSMQ binding, VerifyQueues() creates the queue if required.

Using QueuedServiceHelper, Example 5 is reduced to:

QueuedServiceHelper.VerifyQueues();

MyContractClient proxy = new MyContractClient();
proxy.MyMethod();
proxy.Close();

Note that the client application needs to call QueuedServiceHelper.VerifyQueues() just once anywhere in the application, before issuing the queued calls.

If the client is not using a config file to create the proxy (or is using a channel factory), the client can still use the extrusion method VerifyQueue() of the ServiceEndpoint class:

EndpointAddress address = new EndpointAddress(...);
Binding binding = new NetMsmqBinding(...); //Can still read binding from config

MyContractClient proxy = new MyContractClient(binding,address);
proxy.Endpoint.VerifyQueue();

proxy.MyMethod();

proxy.Close();


3.3. Queue purging

When a host is launched, it may already have messages in queues, received by MSMQ while the host was offline, and the host will immediately start processing these messages. Dealing with this very scenario is one of the core features of queued services, as it enables you to have disconnected services. While this is, therefore, exactly the sort of behavior you would like when deploying a queued service, it is typically a hindrance in debugging. Imagine a debug session of a queued service. The client issues a few calls and the service begins processing the first call, but while stepping through the code you notice a defect. You stop debugging, change the service code, and relaunch the host, only to have it process the remaining messages in the queue from the previous debug session, even if those messages break the new service code. Usually, messages from one debug session should not seed the next one. The solution is to programmatically purge the queues when the host shuts down, in debug mode only. You can streamline this with my ServiceHost<T>, as shown in Example 7.

Example 7. Purging the queues on host shutdown during debugging
public static class QueuedServiceHelper
{
public static void PurgeQueue(ServiceEndpoint endpoint)
{
if(endpoint.Binding is NetMsmqBinding)
{
string queueName = GetQueueFromUri(endpoint.Address.Uri);
if(MessageQueue.Exists(queueName) == true)
{
MessageQueue queue = new MessageQueue(queueName);
queue.Purge();
}
}
}
//More members
}
public class ServiceHost<T> : ServiceHost
{
protected override void OnClosing()
{
PurgeQueues();
//More cleanup if necessary
base.OnClosing();
}
[Conditional("DEBUG")]
void PurgeQueues()
{
foreach(ServiceEndpoint endpoint in Description.Endpoints)
{
QueuedServiceHelper.PurgeQueue(endpoint);
}
}
//More members
}


In this example, the QueuedServiceHelper class offers the static method PurgeQueue(). As its name implies, PurgeQueue() accepts a service endpoint. If the binding used by that endpoint is the NetMsmqBinding, PurgeQueue() extracts the queue name out of the endpoint’s address, creates a new MessageQueue object, and purges it. ServiceHost<T> overrides the OnClosing() method, which is called when the host shuts down gracefully. It then calls the private PurgeQueues() method. PurgeQueues() is marked with the Conditional attribute, using DEBUG as a condition. This means that while the body of PurgeQueues() always compiles, its call sites are conditioned on the DEBUG symbol. In debug mode only, OnClosing() will actually call PurgeQueues(). PurgeQueues() iterates over all endpoints of the host, calling QueuedServiceHelper.PurgeQueue() on each.


Note: The Conditional attribute is the preferred way in .NET for using conditional compilation and avoiding the pitfalls of explicit conditional compilation with #if.
3.4. Queues, services, and endpoints

WCF requires you to always dedicate a queue per endpoint for each service. This means a service with two contracts needs two queues for the two corresponding endpoints:

<service name  = "MyService">
<endpoint
address = "net.msmq://localhost/private/MyServiceQueue1"
binding = "netMsmqBinding"
contract = "IMyContract"
/>
<endpoint
address = "net.msmq://localhost/private/MyServiceQueue2"
binding = "netMsmqBinding"
contract = "IMyOtherContract"
/>
</service>

The reason is that the client actually interacts with a queue, not a service endpoint. In fact, there may not even be a service at all; there may only be a queue. Two distinct endpoints cannot share queues because they will get each other’s messages. Since the WCF messages in the MSMQ messages will not match, WCF will silently discard those messages it deems invalid, and you will lose the calls. Much the same way, two polymorphic endpoints on two services cannot share a queue, because they will eat each other’s messages.

3.5. Exposing metadata

WCF cannot exchange metadata over MSMQ. Consequently, it is customary for even a service that will always have only queued calls to also expose a MEX endpoint or to enable metadata exchange over HTTP-GET, because the service’s clients still need a way to retrieve the service description and bind against it.

3.6. WAS hosting

When hosting a queued service in the WAS, the name of the queue must be the same as the name of the .svc file leading to the virtual directory. For example:

<endpoint
address = "net.msmq://localhost/private/WASService.svc"
binding = "netMsmqBinding"
contract = "IMyQueuedContract"
/>

Other  
 
Most View
How To Unsend A Direct Message On Twitter
Sony NEX-F3 Review (Part 3)
Sony NEX-6 Mirrorless Camera Review (Part 10)
Sony Vaio Pro 11 New High-End Ultrabook Review (Part 2)
Gaming Headset Steel-series H Wireless
The Perfect Enclosure (Part 2) : Cooler Master HAF 912, Corsair Carbide 400R, Cooler Master HAF XB
Better Than Expected Showing (Part 1)
Microsoft Windows Server 2008 R2 : Active directory administration basics
The Roundup Of 120mm Fans: 1,350RPM Speed And More (Part 5)
The Internet Of The Future (Part 2)
Top 10
Sharepoint 2013 : Farm Management - Disable a Timer Job,Start a Timer Job, Set the Schedule for a Timer Job
Sharepoint 2013 : Farm Management - Display Available Timer Jobs on the Farm, Get a Specific Timer Job, Enable a Timer Job
Sharepoint 2013 : Farm Management - Review Workflow Configuration Settings,Modify Workflow Configuration Settings
Sharepoint 2013 : Farm Management - Review SharePoint Designer Settings, Configure SharePoint Designer Settings
Sharepoint 2013 : Farm Management - Remove a Managed Path, Merge Log Files, End the Current Log File
SQL Server 2012 : Policy Based Management - Evaluating Policies
SQL Server 2012 : Defining Policies (part 3) - Creating Policies
SQL Server 2012 : Defining Policies (part 2) - Conditions
SQL Server 2012 : Defining Policies (part 1) - Management Facets
Microsoft Exchange Server 2010 : Configuring Anti-Spam and Message Filtering Options (part 4) - Preventing Internal Servers from Being Filtered