I have a windows service application in .NET 4.6. Inside this windows service, I host a WCF REST API. The API itself exposes an endpoint that makes a call to an external SOAP service. The SOAP client for this service is auto generated by adding Service Reference. The setup is the following:
// The data contract
[DataContract]
public class MyDto
{
[DataMember(Name = "M")]
public string MyProperty { get; set; }
}
// Service definition
[ServiceContract]
public interface IMyService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "mymethod", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
Task<MyDto> MyMethod(MyDto input);
}
// Service implementation (async)
//[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] Commented Concurrency mode
public class MyService : IMyService
{
public async Task<MyDto> MyMethod(MyDto input)
{
DateTime serviceStart = DateTime.Now;
var client = new MyExternalSoapServiceReference.MyExternalSoapServicePortTypeClient();
var result = await client.ExternalCall(input.MyProperty);
input.MyProperty = result.ResponseProperty;
DateTime serviceEnd = DateTime.Now;
// log serviceStart & serviceEnd timestamps
return input;
}
}
// Setup wcf host
WebServiceHost host = new WebServiceHost(typeof(MyService), new Uri("http://localhost:8080"));
/*
Commented throttling behavior
System.ServiceModel.Description.ServiceThrottlingBehavior throttlingBehavior = new System.ServiceModel.Description.ServiceThrottlingBehavior();
throttlingBehavior.MaxConcurrentCalls = 1000;
throttlingBehavior.MaxConcurrentInstances = 2000;
throttlingBehavior.MaxConcurrentSessions = 1000;
host.Description.Behaviors.Add(throttlingBehavior);
*/
host.AddServiceEndpoint(typeof(IMyService), new WebHttpBinding(), "/myapi");
host.Open();
I face problems regarding WCF and the way it handles async operations. It seems that WCF cannot handle properly a batch of concurrent requests. To be more specific, I do not care about the actual processing, rather than the “acceptance” of the requests by WCF. To be more specific, by “acceptance” I mean the moment that WCF starts processing a request, passing the request to my handling method for processing.
To test my WCF endpoint, I have created a sample console app that requests my REST endpoint as following:
// Test client snippet
List<Task> tasks = new List<Task>();
for (int i = 0; i < 500; i++)
{
Task t = Task.Run(async () =>
{
await MakeRequest();
});
tasks.Add(t);
}
Task.WaitAll(tasks.ToArray());
private async Task<string> MakeRequest()
{
using (HttpClient client = new HttpClient())
{
DateTime clientStart = DateTime.Now;
HttpResponseMessage response = await client.PostAsync("http://localhost:8080/myapi/mymethod",
new StringContent(SerializationUtils.SerializeToJsonString(new { M = "1" }),
Encoding.UTF8,
"application/json"));
DateTime clientEnd = DateTime.Now;
// log clientStart & clientEnd timestamps
}
}
All operations are async:
- client requests are async
- service endpoint implementation is async
- the call to the external SOAP service is also made async way
At my test client level, I log the clientStart/clientEnd timestamps. At service endpoint level, I also log the serverStart/serverEnd timestamps.
Since all involved operations are async, I would expect that clientStart timestamps should be very close (nearly at same timestamp), meaning that http requests “leave” test client (nearly) at the same time.
In the same fashion, I would expect that serverStart timestamps should be very close as well, meaning that the requests “arrive” to my endpoint (nearly) at the same time.
Experiment 1
Having this setup, I run the 500 concurrent requests and log the timestamps for both client and wcf api. I noticed that only clientStart timestamps are very close. The serverStart timestamps disperse from 1 – 40sec.
Experiment 2
Having the same setup, I replace the external SOAP call
var result = await client.ExternalCall(input.MyProperty);
with this
await Task.Delay(2000); // add an async delay to mock SOAP call
Again, I have the same observations with experiment 1.
Experiment 3
After some trial & error, I enabled throttling behavior (commented lines), in order to have enough limit >> 500 for concurrency, and also uncommented the Concurrency mode attribute (service annotation). Again, I got the same observations with experiments 1 & 2.
Experiment 4
Having same setup with experiment 3, I replaced again the SOAP call with the Task.Delay. I noticed that indeed all 500 requests “arrived” at the same time in wcf endpoint!
My question is about interpreting the results of the four experiments: why do requests not arrive at same time for experiments 1-3? It seems logical to me, that since all operations are async, then WCF should accept the incoming request rate and process the 500 requests concurrently. I don’t care about the execution time, since this strictly depends on the external service. I am focusing here only on the “arrival” timestamps of the requests. Shouldn’t all 500 requests arrive concurrently?
Another question is about the experiment 4: there seems to be a dependency between the “arrival” of requests in wcf and what the operation involves (actual SOAP call or Task.Delay). It seems logical to me that there should not exists such a dependency at all.
I have also tried the link:
https://learn.microsoft.com/el-gr/troubleshoot/developer/dotnet/wcf/service-scale-up-slowly
which states that there may be a possible issue with earlier WCF implementations about async request handling. I have tried the proposed workaround but I could not see any difference.
Thank you in advance!
George Leontopoulos is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.