Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClient and socket exhaustion - clarification?

This article says that we should use a static HttpClient in order to reuse sockets.

But the first comment there says that there is a DNS changes recognition issue, and the solution is in another article here:

The second article suggested :

var client = new HttpClient();
client.DefaultRequestHeaders.ConnectionClose = true; 

Which controls the KeepAlive header. But suffers from preventing you to take advantage of benefits of re-using a socket

Another solution was :

var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab"));
sp.ConnectionLeaseTimeout = 60*1000; // 1 minute

BUT:

He didn't say whether should we use new Httpclient each time we want to make a request, or should we still use the static one.

Question:

Say I want to use this solution :

var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab"));
sp.ConnectionLeaseTimeout = 60*1000; // 1 minute 
  • Should I still use the static HttpClient approach ? or should I new HttpClient each time I want to make a call? Or - Should I create static/not static new HttpClient for each scheme://basedns ?

He showed the problem but his Conclusion doesn't show the whole right final solution.

Please notice - I'm asking about .net framework. not .net Core.

like image 960
Royi Namir Avatar asked Oct 17 '19 08:10

Royi Namir


People also ask

Why is httpclient not working after socket exhaustion?

So, the HttpClient object is disposed (as it is under using block and the HttpClient implements IDisposable interface) but the socket connection is not disposed yet. That may lead to the Socket exhaustion exception. Though HttpClient implements IDisposable interface, but the recommended approach is not to dispose it after each external api call.

Is it safe to use httpclient with ‘using statement’?

As a first issue, while this class is disposable, using it with the using statement is not the best choice because even when you dispose HttpClient object, the underlying socket is not immediately released and can cause a serious issue named ‘sockets exhaustion’.

What is the best way to solve httpclient performance issues?

Possible approaches to solve that problem are based on the creation of the HttpClient object as singleton or static, as explained in this Microsoft article on HttpClient usage. This can be a good solution for short-lived console apps or similar, that run a few times a day.

Should we use a static httpclient to reuse sockets?

This article says that we should use a static HttpClient in order to reuse sockets. But the first comment there says that there is a DNS changes recognition issue, and the solution is in another


1 Answers

That's a very old article that does explain why HttpClient should be reused, but doesn't explain how to handle DNS changes. That's explained in Singleton HttpClient? Beware of this serious behaviour and how to fix it. That still only deals with one connection.

The obvious answer is to avoid singleton HttpClients but still reuse them for some time. Besides, different sockets are used to connect to different servers, which means we'd really need to reuse (and recycle) sockets per server. The solution came later in the form of HttpClientFactory.

The nice thing is that HttpClientFactory is a .NET Standard 2.0 package, Microsoft.Extensions.Http that can be used by both .NET Core and .NET Old, not just ASP.NET Core applications. I use it in console applications for example.

A very good introduction is HttpClientFactory in ASP.NET Core 2.1 (PART 1) by Steve Gordon, and all his articles in that series.

In short, each time we want an HttpClient we ask the factory for an instance :

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public ValuesController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();
        var result = await client.GetStringAsync("http://www.google.com");
        return Ok(result);
    }
}

HttpClient delegates work to a SocketClientHandler. That's what needs to be reused. HttpClientFactory produces HttpClient instances that reuse Socket handlers from a pool of socket handlers. The handlers are recycled periodically to take care of DNS changes.

Even better, HttpClientFactory can be combined with Polly to add retry logic to the HttpClient instances. It does this behind the scenes by configuring the handlers.

like image 197
Panagiotis Kanavos Avatar answered Sep 18 '22 23:09

Panagiotis Kanavos