In this post, you will find answers to what's the best way to use HttpClient for multiple requests and how much performance cost is associated based on how it is used.
Recently, my team was working on a project that needed numerous HTTP requests to be made. The initial implementation had a new HttpClient
object being created for every request being made. It looked to have some performance cost attached to it that led us to evaluate the effect of using single vs multiple instances of HttpClient
.
Problem Statement
Whats the best way to use HttpClient
for multiple requests? How much performance cost is associated based on how it is used?
Assessment
Went through the Microsoft documentation, which seemed updated based on last when I read few years back. Found a fineprint for myself that states:
Quote:
HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors.
This was a straight give away that we should use a single instance HttpClient
– irrespective of a usecase, one would want to keep a distance from SocketException
errors (though probability of it would be high for heavy usage of HTTP requests).
Now, the query was how to have single HttpClient
for multiple requests but with different request payload for the calls? Also, does this have any impact on performance of the calls and if so, how much?
Resolution
Performance of the two was first thing I looked into. Created a test application that helped evaluate the time taken for various number of requests. Tried with http://www.google.com but seems they have some kind of check at 1000 requests, so went ahead with http://www.bing.com that looked uniform till 5000 requests that I tried with.
for (var i = 0; i < noOfConnections; i++)
{
using (var httpClient = new HttpClient())
{
var result = httpClient.GetAsync(new Uri("http://www.bing.com/")).Result;
}
}
for (var i = 0; i < noOfConnections; i++)
{
var result = _httpClient.GetAsync(new Uri("http://www.bing.com/")).Result;
}
With the above, I got the following numbers on an average post few runs:
No of Requests | Multiple Instance (s) | Single Instance (s) | %age Diff |
100 | 20 | 16.67 | 16.65 |
500 | 103 | 88 | 14.56 |
1000 | 216 | 174 | 19.44 |
2000 | 430 | 351 | 18.37 |
5000 | 1032 | 906 | 12.21 |
It looked like the difference peaked around 1000 requests and overall, there was an improvement with single instance.
Now, given we had a usecase where multiple HTTP requests has to be made simultaneously but with different payloads, looked at how to achieve it with single instance. Keeping multiple types of requests, unit testing, high load – one possible way looked like below that worked out well for us:
public class HttpClientManager : IHttpClientManager
{
...
public HttpClientManager(HttpMessageHandler messageHandler)
{
_httpClient = new HttpClient(messageHandler);
}
private HttpRequestMessage SetupRequest(IRequestPayload requestPayload)
{
var request = new HttpRequestMessage
{
RequestUri = new Uri(requestPayload.Url)
};
switch (requestPayload.RequestType)
{
case RequestType.POST_ASYNC:
request.Method = HttpMethod.Post;
request.Content = GetHttpContent(requestPayload.ContentJson);
break;
case RequestType.PUT_ASYNC:
request.Method = HttpMethod.Put;
request.Content = GetHttpContent(requestPayload.ContentJson);
break;
case RequestType.DELETE_ASYNC:
request.Method = HttpMethod.Delete;
break;
case RequestType.GET_ASYNC:
request.Method = HttpMethod.Get;
break;
default:
request.Method = HttpMethod.Get;
break;
}
...
}
public HttpResponseMessage ExecuteRequest(IRequestPayload requestPayload)
{
HttpRequestMessage httpRequestMessage = SetupRequest(requestPayload);
HttpResponseMessage httpResponseMessage = _httpClient.SendAsync
(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead).Result;
return httpResponseMessage;
}
private HttpContent GetHttpContent(string contentJson)
{
return new StringContent(contentJson, ENCODING, MEDIATYPE_JSON);
}
}
Given that there are numerous articles on the web explaining details of the entire HttpClient
workflow and inner details, I will not cover that here but a quick explanation on couple of key info.
In the code above:
HttpRequestMessage
is used to setup HttpClient
object based on our need. We make use of the fact that HttpRequestMessage
can be used only once. After the request is sent, it is disposed immediately to ensure that any associated Content
object is disposed.
Making use of HttpClient
underlying implementation, have used HttpMessageHandler
more from the unit test point of view.
Conclusion
One should use a single instance of HttpClient
at application level and avoid create/destroy of it multiple times. Further, this also has better performance with more than 12% improvement based on the load.
For multiple requests of different payloads, having a single instance HttpClient
but a new HttpRequestMessage
for every request looked liked a good approach to use.
The entire code for lookup can be downloaded from here.
Note: For .NET Core, Microsoft added a new interface around the same discussion to have better handle at HttpClient instance: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
Published Blog