Search code examples
asp.netasp.net-mvc-4singletonhttpclientweb-optimization

Which type of singleton pattern should be used for creating HTTP client for my web application


I have a web application. I found that performance bottleneck could be that i am creating Http client again and again for every request.

public static class DemoHttpClient
    {
       public static HttpClient GetClient()
       {
           HttpClient client = new HttpClient();
           client.BaseAddress = new Uri(DemoConstants.DemoAPI);
           client.DefaultRequestHeaders.Accept.Clear();
           client.DefaultRequestHeaders.Accept.Add(
                new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

           return client;
}
    }

public class DemoConstants
{
    public const string DemoAPI = "http://localhost/";
}

I am planning to implement singleton for this. And found this very helpful article. http://csharpindepth.com/Articles/General/Singleton.aspx

I am confused as to how exactly ASP.NET MVC web application lifecycle is with when it is deployed on the server. Assuming there will be multiple threads calling same resource, the resource further again and again making new http clients..

What should we do here.. 1) Lazily load HTTP client? 2) Not lazily load it?

Which particular approach should we use?


Solution

  • This doesn't sound like a good idea. In particular, take a peek into the docs of the HttpClient class:

    Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

    https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.118%29.aspx

    This means that accessing the very same singleton instance from multiple threads will lead to undefined issues.

    What you could do however, is you could reuse the same instance across a single request. This can be done by storing an instance in the Items container:

       private static string ITEMSKEY = "____hclient";
    
       public static HttpClient GetClient()
       {
           if ( HttpContext.Current.Items[ITEMSKEY] == null )
           {
              HttpClient client = new HttpClient();
              client.BaseAddress = new Uri(DemoConstants.DemoAPI);
              client.DefaultRequestHeaders.Accept.Clear();
              client.DefaultRequestHeaders.Accept.Add(
                new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
    
              HttpContext.Current.Items.Add( ITEMSKEY, client );
           }
    
           return (HttpClient)HttpContext.Current.Items[ITEMSKEY];
        }
    

    Note, that since the HttpClient implements IDisposable, it still could be a good idea to dispose such instance somewhere in the pipeline, for example in the EndRequest event of the application pipeline.

    Update: as noted in a comment by @LukeH, the updated version of the docs for the .NET 4.5 and 4.6 states that some of methods of the HttpClient class are thread safe:

    https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.110%29.aspx

    The updated remarks section states that a single instance is basically a collection of shared settings applied to all requests executed by this instance. Then, the docs says:

    In addition, every HttpClient instance uses its own connection pool, isolating its requests from requests executed by other HttpClient instances.

    This means that the isolation of different pools could still make sense, my personal recommendation would still be then to not to have a singleton, as you possibly would still need to change some settings between consecutive requests.