Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

HttpClient Single Instance or Multiple

0.00/5 (No votes)
30 Jun 2020 1  
Evaluating the effect of using single vs multiple instances of HttpClient
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;
    }
}
//having private static readonly HttpClient _httpClient = new HttpClient();

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:

// Single instance of HttpClientManager was setup  
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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here