Introduction and goal
In this article, we will concentrate on
WCF concurrency and
throttling. We will first try to understand what is
WCF concurrency and the three important types of
WCF concurrency.
We will then see a small sample of
WCF concurrency with Single and Multiple. We will then go through 9 combinations of
WCF concurrency and instancing.
Finally we will try to understand how to configure
throttling using the
WCF ‘
web.config’ file.
Why do we need concurrency in WCF?
If you search on the web for the dictionary meaning of
concurrency, you will find the following definition:
“Happening at the same time”
WCF concurrency helps us configure how
WCF service instances can serve multiple requests at the same time. You will need
WCF concurrency for the below prime reasons;
there can be other reasons as well but these stand out as important reasons:
- Increase throughput: Many times you want to increase the amount of work your WCF service instance does at any moment of time.
In other words, you would like to increase the throughput. Throughput means how much work a given thing can do.
By default, a WCF service handles only one request at a given moment of time.
- Integration with a legacy system: Many times your WCF
services interact with legacy systems like VB6, COM, etc.
It’s very much possible that these systems are not multithreaded, in
other words they handle only one request at any given time. So even
though your WCF service has concurrent capabilities,
you would still like to handle one request at a time. This is achieved by using throttling in combination with WCF concurrency capabilities.
WCF concurrency and instancing – Two different things
WCF instancing and
WCF concurrency
are two different things.
WCF instance dictates how objects are created while
WCF concurrency dictates how many requests can be handled by
WCF objects.
I do understand that many of our developer friends know this difference, but as you go deeper into
WCF concurrency, there is lot of connection with
WCF instancing also,
so I wanted to just emphasize the differences.
WCF instancing gives three levels of
WCF object instance controls: per call, per session, and single. In similar lines,
WCF also has three ways of implementing
WCF concurrency.
Three types of WCF concurrency
There are three ways by which you can handle
concurrency in
WCF: single, multiple, and reentrant. To specify
WCF concurrency, we need to use the
ServiceBehavior
tag as shown below, with the appropriate ‘
ConCurrencyMode
’ property value.
Single: A single request has access to the
WCF service object at a given moment of time. So only one request will be processed at any given moment of time.
The other requests have to wait until the request processed by the
WCF service is completed.
Multiple: In this scenario, multiple requests can be handled by the
WCF service object at any given moment of time.
In other words, requests are processed at the same time by spawning multiple threads on the
WCF server object. So you have great throughput here
but you need to ensure
concurrency issues related to
WCF server objects.
Reentrant: A single request thread has access to the
WCF service object, but the thread can exit the
WCF service to call another
WCF service or can also call a
WCF client through callback and reenter without deadlock.
Sample code demonstration
In order to demonstrate this, let’s create a simple sample code
as shown below. We will create a simple
WCF service with a method name
Call
. When a client calls this
WCF service, it will display the following details:
- Client name that made the call. This value will be provided as an input when the client wants to make a call to the WCF service.
- Instance number, this will represent the number of WCF instances which are serving the request.
- Thread number which is executing the method.
- Time when the
Call
method was actually called.
Below is the service contract of the
Call
method. Please note that
OperationContract
is defined with
IsOneWay
as
true
.
[ServiceContract]
public interface IHelloWorldService
{
[OperationContract(IsOneWay=true)]
void Call(string ClientName);
}
Below is a simple implementation of the service contract
IHelloWorldService
interface. It has an instance variable
i
which helps us maintain
the instance counter and a simple
Console.Writeline
which displays the client name, instance number, thread number, and time when the method was called.
This method waits for 5 seconds using the ‘
Thread.Sleep
’ function.
public class HelloWorldService : IHelloWorldService
{
public int i;
public void Call(string ClientName)
{
i++;
Console.WriteLine("Client name :" + ClientName + " Instance:" +
i.ToString() + " Thread:" + Thread.CurrentThread.ManagedThreadId.ToString() +
" Time:" + DateTime.Now.ToString() + "\n\n");
Thread.Sleep(5000);
}
}
This WCF service will be self hosted using WsHttpBinding
as shown below. We have chosen
self hosting so that we can monitor the time, threads,
and number of instances of the WCF service.
static void Main(string[] args)
{
Uri httpUrl = new Uri("http://localhost:8010/MyService/HelloWorld");
ServiceHost host = new ServiceHost(typeof(ClassLibrary1.HelloWorldService), httpUrl);
host.AddServiceEndpoint(typeof(ClassLibrary1.IHelloWorldService), new WSHttpBinding(), "");
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
host.Open();
Console.WriteLine("Service is host at " + DateTime.Now.ToString());
Console.WriteLine("Host is running... Press <Enter> key to stop");
Console.ReadLine();
}
From the client side, we will make five continuous calls simultaneously on the WCF service.
Below is the code snippet for this:
Console.WriteLine("Enter Client name");
string str = Console.ReadLine();
ServiceReference1.HelloWorldServiceClient obj =
new ServiceReference1.HelloWorldServiceClient();
for(int i=0;i<5;i++)
{
obj.Call(str);
}
If you run the above project and monitor your
WCF server service, you will see the screenshot. There are two important points to note:
- By default the WCF service is configured to run with per call. So new instances are created every time the service runs.
Instance 1 indicates new instances created for every method call.
- By default the concurrency is single so the same thread is used to service all five requests which are sent from the WCF client side. You can see the value of the thread is always 4.
- The most important factor is time. As concurrency is configured as single, it will be called one after another sequentially.
You can notice this from the time difference of method calls of 5 seconds.
Let’s go and change the
concurrency mode to multiple. In order to change the
concurrency mode to multiple, we need to specify
Multiple
as the
concurrency mode
as shown in the below code snippet.
If you run the client now, you can see different threads (i.e.,
thread 4, 5, 6, 7, and 8) are spawned to serve the request and the time
of the method calls
are almost same. In other words, the methods are executed concurrently
on different threads.
In short, you are now having a higher throughput in less amount of time.
9 combinations of Instancing and Concurrency
There are 9 combinations of
concurrency and instancing as shown in the below table. In the sections below, we will discuss this in more detail.
InstanceContext Mode |
ConcurrencyMode |
Single (Default) |
Multiple |
Reentrant |
Single (Single instance for all clients) |
Single thread for all clients. |
Multiple threads for all clients. |
Single threads for all clients, locks are released when calls diverted to other WCF services. |
PerSession
(Default) (Multiple instance per client) |
Single thread for every client. |
Multiple threads for every request. |
Single threads for all clients, locks are released when calls diverted to other WCF services. |
PerCall (Multiple instances for every method call) |
Single thread for every client. |
Single thread for every client |
Single threads for all clients, locks are released when calls diverted to other WCF services. |
Instance mode = Per Call and Concurrency = Single
With Per Call, new
WCF instances are created for every method call made to the
WCF server service. The default
concurrency is Single so only one thread will be used to serve all instances.
Below is a simple pictorial representation of what will happen in Per Call and Single
concurrency scenarios:
- For every client instance, a single thread will be allocated.
- For every method call, a new service instance will be created.
- A single thread will be used to serve all WCF instances generated from a single client instance.
If you refer the previous sample, you can see how threads are the same and the halt of 5 seconds on the
WCF service.
Instance mode = Per Call and Concurrency = Multiple
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class HelloWorldService : IHelloWorldService
{
}
In this combination, multiple instances are created for every call but multiple threads serve every method call to a
WCF service instance. You can see in the below
figure we have two
WCF service instances and every instance has multiple
WCF service objects created for every method call. Every method call is handled by multiple threads.
In the above sample, if you remember, we have multiple threads
serving every method call and the time difference between every method
call is reduced considerably.
The method calls are fired approximately at the same time.
Instance mode = Per Session and Concurrency = Single
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,
ConcurrencyMode = ConcurrencyMode.Single)]
public class HelloWorldService : IHelloWorldService
{
}
In this combination, one
WCF service instance is created for every
WCF client session because the
WCF
instance mode is set to per session. All the method calls
are executed in a sequential manner one by one. In other words, only one
thread is available for all method calls for a particular service
instance.
If you run the sample code with Per Session and Single combination
mode, you will find the same thread executes all requests with the same
WCF instance per session.
Instance mode = Per Session and Concurrency = Multiple
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class HelloWorldService : IHelloWorldService
{
}
Instance mode ‘
PerSession
’
concurrency is the default setting in
WCF
In this combination, one
WCF instance is created for every
WCF client session and every method call is run over multiple threads. Below is a pictorial representation:
If you run the sample code attached with this article, you will find
the same instance with every method call running on different methods.
To get a better idea, you can run different client exe instances with different names as shown below.
You will notice how every client gets its own
WCF service instance with every method allocated to run on different threads.
Instance mode = Single and Concurrency = Single
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class HelloWorldService : IHelloWorldService
{
}
In this combination, only one instance of a
WCF service instance is created which serves all requests which are sent from all
WCF clients. These requests are served using only one thread.
You can see in the below figure approximately one thread is assigned for every
WCF client call and only one instance of the
WCF service is created.
Instance mode = Single and Concurrency = Multiple
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class HelloWorldService : IHelloWorldService
{}
In this combination, one
WCF service instance is created for serving all
WCF clients. All requests are served using multiple / different threads.
You can see from the output that we have 6 threads serving 10
requests, as compared to previously we had approximately only two
threads.
Reentrant
In this mode, only one thread runs to serve all requests. If your
WCF service makes an outbound call to some other
WCF service, or makes a client call,
it releases the thread lock. In other words, until the outbound call is completed, other
WCF clients can not make calls.
Throttling behavior
WCF throttling helps you to put an upper limit on the number of concurrent calls,
WCF instances, and concurrent sessions.
WCF provides
three ways by which you can define upper limits:
MaxConcurrentCalls
,
MaxConcurrentInstances
, and
MaxConcurrentSessions
.
MaxConcurrentCalls
: Limits the number of concurrent requests that can be processed by WCF service instances.
MaxConcurrentInstances
: Limits the number of service
instances that can be allocated at a given time. When it’s PerCall
services,
this value matches the number of concurrent calls. For PerSession
services, this value equals the number of active session instances.
This setting doesn’t matter for Single instancing mode, because only one
instance is ever created.
MaxConcurrentSessions
: Limits the number of active sessions allowed for the service.
All the above three settings can be defined in the
servicebehaviors
tag as shown in the below XML snippet:
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceThrottling maxConcurrentCalls="16"
maxConcurrentInstances="2147483647" maxConcurrentSessions="10" />
</behavior>
</serviceBehaviors>
Default values for WCF throttling
Below is a simple table which shows the default settings for
throttling for different
WCF versions:
|
MaxConcurrentSessions |
MaxConcurrentSessions |
MaxConcurrentSessions |
WCF 3.0 / 3.5 |
6 |
26 |
10 |
WCF 4.0 |
16 * processorcount MaxConcurrentCalls |
MaxConcurrentCalls + MaxConcurrentSessions 100 * processorcount |
100 * processorcount |