1

I have a simple webapp hosted on Azure (app service) that makes calls to two different external APIs. Yesterday we had a huge expected spike in traffic and the whole thing came crashing down which I now realize is because I was creating and disposing of new instances of HttpClient like so:

var url = "https://www.externalapi.com/someendpoint" +
    "/takethedata.js?" +
    "[form]email=" + visitor.EmailAddress +
    "&[form]name=" + visitor.FirstName + " " + visitor.LastName +
    "&[form]zip=" + visitor.zip +
    "&[form]location=" + city + ", " + state;

using(var handler = new HttpClientHandler())
{
    handler.ClientCertificateOptions = ClientCertificateOption.Manual;
    handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
    handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12 |
        System.Security.Authentication.SslProtocols.Tls11 |
        System.Security.Authentication.SslProtocols.Tls;

    using(var client = new HttpClient(handler))
    {
        client.BaseAddress = new Uri(url);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        HttpResponseMessage response = await client.GetAsync(url);

    }
}

In the same controller in another method I am making another external API in the same way:

string AuthToken = "2678c7b5-3cvt-XXXX-XXXX-XXXXXXXXX";
string BaseUrl = "https://someotherdomain.com/dosomething/" + AuthToken;

var jsonContent = "{ \"param1\": \"" + value1 + "\", \"Timeout\":\"15\", \"Verbose\":\"True\" }";

using(var client = new HttpClient())
{
    client.BaseAddress = new Uri(BaseUrl);
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

    HttpResponseMessage response = await client.PostAsync(BaseUrl,
        new StringContent(jsonContent, Encoding.UTF8, "application/json"));

    if (!response.IsSuccessStatusCode)
    {
        return Json("Email Check Error");
    }
    else
    {
        var responseContent = await response.Content.ReadAsStringAsync();

        return Json(responseContent);
    }
}

I've been reading about the HttpClient and creating a singleton per external API? And issues with setting different URIs etc. and I am really confused on how to proceed. If I just dump the "using" will it potentially resolve? If I need to create a singleton is there anywhere that demonstrates how I might do this and have it apply to my scenario? Thanks!

Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
billy jean
  • 1,399
  • 4
  • 25
  • 45
  • 1
    The recommendation is to create a singleton per URL host (the 'domain' portion of the URL. You're right that it is confusing to manage so look at [the Flurl library](https://tmenier.github.io/Flurl/) and it's per host client factory pattern. It is an amazing wrapper around HttpClient that manages the caching complexity for you. – Sixto Saez Mar 20 '18 at 15:27

1 Answers1

5

What exactly are you trying to do? If I understand correctly, you just want to make another request with the same HttpClient on a different URI, is that correct?

In that case you can just call the appropriate request function using the HttpClient object, like so for example (GET request):

HttpResponseMessage response = await client.GetAsync(url);

Try to initialize the client object in the constructor and then keep using the same instance if you think that reinstantiating it each time you send a request is the problem?

zlikar
  • 84
  • 2
  • I know for a fact that creating new instances of HttpClient is making my app service app on Azure throw 502.3 errors because the servers become unresponsive under heavy load (50+ requests per second in my case). What people seem to be doing is to create a singleton and inject it into the controller. https://stackoverflow.com/questions/22560971/what-is-the-overhead-of-creating-a-new-httpclient-per-call-in-a-webapi-client and https://pastebin.com/9ypJcw9X but there seems to be some confusion with how it works with different URI's per controller call from the originating client.. as in my case – billy jean Mar 20 '18 at 15:32
  • This is how I used it in my own project a while back: create a handler and the client object in the constructor and call the same instance each time I make a request. Though my settings (certificate options, protocols) differ a bit from yours, so that might be the problem? – zlikar Mar 20 '18 at 15:43
  • Might also be a problem with the BaseAddress property? https://stackoverflow.com/questions/23438416/why-is-httpclient-baseaddress-not-working – zlikar Mar 20 '18 at 15:47
  • 1
    Yeah, don't use base address if you are working with multiple sites. Your default headers are the same. You only need to decide if your cert settings are compatible. – Tratcher Mar 20 '18 at 20:32
  • @zl1k4r well everything worked fine until we had a huge number of requests. Then wrapping in using killed the server. In your scenario using 1 for the controller.. how is the performance? We had a burst of 70k requests. for now we created a dictionary of two httpclients, one for each baseURI. We injected the dictionary into the controller. Seems to be working but not sure if the scaling issue is resolved. – billy jean Mar 20 '18 at 21:48
  • Well I don't get as many requests as you so I cannot give you a suitable answer, sorry. – zlikar Mar 26 '18 at 20:23