How to fix basicHttpBinding in WCF when using multiple proxy clients?
[Question seems a little long but please have patience. It has sample source to explain the problem.]
Consider following code which is essentially a WCF host:
[ServiceContract (Namespace = "http://www.mightycalc.com")]
interface ICalculator
{
[OperationContract]
int Add (int aNum1, int aNum2);
}
[ServiceBehavior (InstanceContextMode = InstanceContextMode.PerCall)]
class Calculator: ICalculator
{
public int Add (int aNum1, int aNum2) {
Thread.Sleep (2000); //Simulate a lengthy operation
return aNum1 + aNum2;
}
}
class Program
{
static void Main (string[] args) {
try {
using (var serviceHost = new ServiceHost (typeof (Calculator))) {
var httpBinding = new BasicHttpBinding (BasicHttpSecurityMode.None);
serviceHost.AddServiceEndpoin开发者_运维知识库t (typeof (ICalculator), httpBinding, "http://172.16.9.191:2221/calc");
serviceHost.Open ();
Console.WriteLine ("Service is running. ENJOY!!!");
Console.WriteLine ("Type 'stop' and hit enter to stop the service.");
Console.ReadLine ();
if (serviceHost.State == CommunicationState.Opened)
serviceHost.Close ();
}
}
catch (Exception e) {
Console.WriteLine (e);
Console.ReadLine ();
}
}
}
Also the WCF client program is:
class Program
{
static int COUNT = 0;
static Timer timer = null;
static void Main (string[] args) {
var threads = new Thread[10];
for (int i = 0; i < threads.Length; i++) {
threads[i] = new Thread (Calculate);
threads[i].Start (null);
}
timer = new Timer (o => Console.WriteLine ("Count: {0}", COUNT), null, 1000, 1000);
Console.ReadLine ();
timer.Dispose ();
}
static void Calculate (object state)
{
var c = new CalculatorClient ("BasicHttpBinding_ICalculator");
c.Open ();
while (true) {
try {
var sum = c.Add (2, 3);
Interlocked.Increment (ref COUNT);
}
catch (Exception ex) {
Console.WriteLine ("Error on thread {0}: {1}", Thread.CurrentThread.Name, ex.GetType ());
break;
}
}
c.Close ();
}
}
Basically, I am creating 10 proxy clients and then repeatedly calling Add service method on separate threads. Now if I run both applications and observe opened TCP connections using netstat, I find that:
- If both client and server are running on same machine, number of tcp connections are equal to number of proxy objects. It means all requests are being served in parallel. Which is good.
- If I run server on a separate machine, I observed that maximum 2 TCP connections are opened regardless of the number of proxy objects I create. Only 2 requests run in parallel. It hurts the processing speed badly.
- If I switch to net.tcp binding, everything works fine (a separate TCP connection for each proxy object even if they are running on different machines).
I am very confused and unable to make the basicHttpBinding use more TCP connections. I know it is a long question, but please help!
After spending 2 days on this problem, I found the solution so let me document it here.
Actually the problem is not of service configuration or throttling. Actually server (WCF host) cannot do anything if a client is not making connection to it. In this case WCF client was not making more than 2 connections. I tried basicHttpBinding, WsHttpBinding and even the good old web reference (asmx) method. Every case failed to make more than 2 connections.
The root of this effect lies in ServicePointManager.DefaultConnectionLimit property which has a default value of 2. This class is in System.Net namespace which is responsible for making Http connections. Apparently both WCF and ASMX web reference use System.Net namespace for doing their HTTP stuff. This explains why I couldn't make multiple service request even after creating multiple proxy clients in multiple threads.
In summary, the solution is to set ServicePointManager.DefaultConnectionLimit
to the number of concurrent calls you want to make from your client.
Check out server throttling - it's a server-side behavior which you can apply to your server-side config:
<system.serviceModel>
<services>
<service
name="Microsoft.WCF.Documentation.SampleService"
behaviorConfiguration="Throttled" >
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/SampleService"/>
</baseAddresses>
</host>
<endpoint
address=""
binding="wsHttpBinding"
contract="Microsoft.WCF.Documentation.ISampleService"
/>
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"
/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Throttled">
<serviceThrottling
maxConcurrentCalls="1"
maxConcurrentSessions="1"
maxConcurrentInstances="1" />
<serviceMetadata httpGetEnabled="true" httpGetUrl="" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
See this blog post for more detailed info, or check the MSDN docs on Service Throttling
精彩评论