HttpClient single instance or multiple

Recently, we were working on a project that needed numerous HTTP requests to be made. 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 and the performance cost associated with it?

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:

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 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 has any impact on performance of the calls and if so, how much?

Resolution:

I started with looking into performance aspect for the two options. Created a test application that helped evaluate the time taken for various number of requests. Tried with www.google.com but seems they have some kind of check at 1000 requests so went ahead with 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 RequestsMultiple Instance (s)Single Instance (s)%age Diff
1002016.6716.65
5001038814.56
100021617419.44
200043035118.37
5000103290612.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);
    }
}

Since 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 to avoid create/destroy of it multiple times. Further, results suggest 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 a good approach to use.

P.S.: 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

Entire code for lookup can be downloaded from here.

HTTP 500 – Internal server error

This is another common error that troubles a lot of ASP.NET users.

Problem Statement

Generally, the questions are framed like:

I receive a HTTP 500 – Internal Server Error  exception while trying to browse my hosted web application. Exception message says “This error (HTTP 500 Internal Server Error) means that the website you are visiting had a server problem which prevented the webpage from displaying”. Please help, how to resolve it?

Assessment

Any HTTP request made to web application running on IIS (or any other web server) returns the status of the response. This HTTP status code indicates about the request success or failure. If the request was unsuccessful, it might provide the reason why so.

Out of various status code, 5xx are the codes related to Server error. They indicate that the server failed to complete the request because server encountered an error.

The 500 Internal Server Error is a very general HTTP status code. It means something has gone wrong on the website and web server is unable to specify what exactly, thus failing in fulfilling the request made by the client. This is not related to client and the fault is in the webpage/website requested that resides on server. This status code can be considered as a ‘catch-all’ server error of Web server.

More details around various HTTP status code: The HTTP status code in IIS 7.0, IIS 7.5, and IIS 8.0

Possible Resolutions

Make sure that internally web server maintains some kind of internal error logs that gives more detail of what went wrong and thus help in diagnosing the issue. Generally, it is logged into Windows Event Logs on the server. Thus, first thing while troubleshooting the error is to see Windows Event Logs on the server to find what went wrong.

Other useful thing to troubleshoot it would be to disable friendly HTTP error messages to see if the raw content can provide a direction to look more. Steps:

  • Go to menu Tools/Internet Options in your IE.
  • Click on the Advanced tab & then uncheck “Show friendly HTTP error messages” option & then click Ok.
  • Now, when on accessing the same web page, much more developer meaningful error message will be shown.

Moving on, following are most common:

Option #1:
HRESULT: 0x80070035 – The page cannot be displayed because an internal server error has occurred.
This occurs because the server that is running IIS cannot access the configured root directory of the requested location.

Resolution would be to make sure that the server that is running IIS can access the configured root directory of the requested location.

Option #2:
HRESULT: 0x800700c1 – The page cannot be displayed because an internal server error has occurred.
This occurs because a script mapping is not valid.

Resolution would be to make sure that the script mapping points to the ISAPI.dll file that can process the request.
To do this, follow these steps:

  1. Click Start, click Run, type inetmgr.exe, and then click OK.
  2. In IIS Manager, expand server name, expand Web sites, and then click the Web site that you want to modify.
  3. In Features view, double-click Handler Mappings.
  4. Make sure that the script mapping points to the correct ISAPI.dll file. (e.g: .asp files should map to the %windir%\system32\inetsrv\asp.dll file)

Option #3:
HRESULT: 0x8007007f – There is a problem with the resource you are looking for, so it cannot be displayed.
This occurs because the handler mapping for the requested resource points to a .dll file that cannot process the request.

Resolution would be to edit the handler mapping for the requested resource to point to the .dll file that can process the request.
To do this, follow these steps:

  1. Click Start, click Run, type inetmgr.exe, and then click OK.
  2. In IIS Manager, expand server name, expand Web sites, and then click the Web site that you want to modify.
  3. In Features view, double-click Handler Mappings.
  4. Right-click the script mapping that you want to edit, and then click Edit.
  5. In the Edit Script Map dialog box, type the appropriate executable file in the Executable box, and then click OK.

Option #4:
One of the other possibilities could be an issue in the way web application is hosted. Some security configuration issue or conflict due to multiple config files.

Resolution would be to make sure application is hosted correctly by published the application as website and setting up the virtual directory as needed.
More details around the known issues and their resolution:
Error message when you visit a Web site that is hosted on IIS 7.0: “HTTP Error 500.0 – Internal Server Error”
Troubleshoot an “HTTP 500 – Internal Server Error” error message on IIS 4.0 or on IIS 5.0

Conclusion

This is a server error and can only be solved by website admin who has access to files and the web-server. There can be one of/or multiple reasons to get this error. One has to track down the issue and handle accordingly.

Keep learning!