It’s rare that a web service has some intensive processor bound computation to execute. Far more common for business applications, is a web service that executes one or more IO intensive operations. Typically our web service would access a database over the network, read and write files, or maybe call another web service. If we execute these operations synchronously, the thread that processes the web service request will spend most of its time waiting on IO. By executing IO operations asynchronously we can free the thread processing the request to process other requests while waiting for the IO operation to complete.
In my experiments with a simple self-hosted WCF service, I’ve been able to demonstrate up to 7000 concurrent connections handled by just 12 threads.
Before I show you how to write an asynchronous WCF service, I want to clear up the commonly held misconception (yes, by me too until a year or so ago), that asynchronous IO operations spawn threads. Many of the APIs in the .NET BCL (Base Class Library) provide asynchronous versions of their methods. So, for example, HttpWebRequest has a BeginGetResponse / EndGetResponse method pair alongside the synchronous method GetResponse. This pattern is called the Asynchronous Programming Model (APM). When the APM supports IO operations, they are implemented using an operating system service called IO Completion Ports (IOCP). IOCP provides a queue where IO operations can be parked while the OS waits for them to complete, and provides a thread pool to handle the completed operations. This means that in-progress IO operations do not consume threads.
The WCF infrastructure allows you to define your operation contracts using APM. Here’s a contract for a GetCustomer operation:
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface ICustomerService
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetCustomerDetails(int customerId, AsyncCallback callback, object state);
Customer EndGetCustomerDetails(IAsyncResult asyncResult);
}
Essentially ‘GetCustomerDetails’ takes a customerId and returns a Customer. In order to create an asynchronous version of the contract I’ve simply followed the APM pattern and created a BeginGetCustomerDetails and an EndGetCustomerDetails. You tell WCF that you are implementing APM by setting AsyncPattern to true on the operation contract.
The IAsyncResult that’s returned from the ‘begin’ method and passed as an argument to the ‘end’ method links the two together. Here’s a simple implementation of IAsyncResult that I’ve used for these experiments, you should be able to use it for any asynchronous WCF service:
public class SimpleAsyncResult<T> : IAsyncResult
{
private readonly object accessLock = new object();
private bool isCompleted = false;
private T result;
public SimpleAsyncResult(object asyncState)
{
AsyncState = asyncState;
}
public T Result
{
get
{
lock (accessLock)
{
return result;
}
}
set
{
lock (accessLock)
{
result = value;
}
}
}
public bool IsCompleted
{
get
{
lock (accessLock)
{
return isCompleted;
}
}
set
{
lock (accessLock)
{
isCompleted = value;
}
}
}
// WCF seems to use the async callback rather than checking the wait handle
// so we can safely return null here.
public WaitHandle AsyncWaitHandle { get { return null; } }
Read more: Code rant
0 comments:
Post a Comment