18

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.

Liam
  • 27,717
  • 28
  • 128
  • 190
Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • 1
    That's a *very* old article that describes the *first* problem. It doesn't contain any solution for the second problem. That solution is to use the HttpClientFactory instead which keeps its own pool of HttpClientHandlers and recycles them periodically. – Panagiotis Kanavos Oct 17 '19 at 08:07
  • @PanagiotisKanavos where do you see the word `HttpClientFactory ` in the article ? also i think you're talking about .net core ( my question is not about core) where you inject typed/name httpclient - or IhttpClientFactory ( where indeed a pool of handlers are used). Also the second problem is explianed in the second link in my question. – Royi Namir Oct 17 '19 at 08:09
  • 2
    I said it's a *very* old article. The problem was handled in the next 3 years by introducing HttpClientFactory. – Panagiotis Kanavos Oct 17 '19 at 08:24

3 Answers3

19

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.

Hugo Nava Kopp
  • 2,906
  • 2
  • 23
  • 41
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 1
    I know about the .net core solution and I've read all Steve's series. but they are all in .net core. my question clearly states about .net framework and not core. so back to the question if I wnt to use his solution with `var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab")); sp.ConnectionLeaseTimeout = 60*1000; // 1 minute` , how should I instantiate `httpClient` ? multiple ? single ? for each endpoint ? – Royi Namir Oct 17 '19 at 08:30
  • They are .NET Standard, not .NET Core. I use them in .NET Old console applications. The answer to your question is *don't* do it yourself, let the factory do it. – Panagiotis Kanavos Oct 17 '19 at 08:31
  • my question is not about .net standard but 4.6.1 – Royi Namir Oct 17 '19 at 08:32
  • Which uses .NET Standard libraries. .NET Standard isn't a runtime, it's an set of APIs that are offered by a runtime like .NET Framework or .NET Core. Libraries targeting .NET Standard work in any runtime that's compatible with the .NET Standard version – Panagiotis Kanavos Oct 17 '19 at 08:33
  • @RoyiNamir you're already using .NET Standard 2.0 libraries one way or another anyway, either directly from Microsoft or other NuGet packages like JSON.NET – Panagiotis Kanavos Oct 17 '19 at 08:34
  • Ok i'll ask the other way around , assuming I don't use .net standard but only .net framework ( and I can't change the project libraries) : how should I instantiate Httpclient when using the `ServicePointManager` solution ? – Royi Namir Oct 17 '19 at 08:35
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/201003/discussion-between-royi-namir-and-panagiotis-kanavos). – Royi Namir Oct 17 '19 at 08:36
  • @RoyiNamir why? You're already using .NET Standard. Neither Microsoft nor OSS authors publish for .NET 4.6 only, all publish for .NET Standard so they *don't* have to compile for multiple runtimes. Which means, that's not a problem. What you ask, lifecycle management for HttpClient is *already* available through HttpClientFactory. Which is open source by the way, allowing you to simply copy the code. Only you don't need to, because you can just add the NuGet package – Panagiotis Kanavos Oct 17 '19 at 08:38
  • @RoyiNamir BTW you'll get a far better HttpClient too - the one included in 4.6 was v1 with multiple issues that were solved in later versions which were published as NuGet packages. – Panagiotis Kanavos Oct 17 '19 at 08:39
  • TLDR for any new readers: .Net Standard 2.0 libraries work in .Net Framwork without changing anything - it just works. – AnorZaken Jul 04 '23 at 08:04
  • @AnorZaken that's what the question and the comments say. Right now the oldest supported .NET Framework version is 4.6.2 which supports .NET Standard 2.0. – Panagiotis Kanavos Jul 04 '23 at 08:08
0

ServicePointManager is flaky in my opinion and is only valid on Windows machines. Effectively be dead code if you go over to dotnet core from dotnet framework. I would not depend my implementation on it.

My suggestion is create a singleton service serving only one instance of HttpClient.

If you are using asp.net core they provide you with an implementation of the static client as well as re-describe the issue in this document

Anton Toshik
  • 2,621
  • 2
  • 19
  • 42
  • Thanks but I'm not using core. Also singleton will have a problem of dns changes... (in .net fw). I know already how to deal with it in .net core but my question is for non core – Royi Namir Oct 17 '19 at 08:26
  • @RoyiNamir how so? Singleton implies that only 1 instance is created effectively giving you a single instance of the `HttpClient` across your whole application. – Anton Toshik Oct 17 '19 at 08:28
  • I know about the .net core solution and I've read all Steve's series. but they are all in .net core. my question clearly states about .net framework and not core. so back to the question if I wnt to use his solution with var sp = `ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab")); sp.ConnectionLeaseTimeout = 60*1000; // 1 minute` , how should I instantiate httpClient ? multiple ? single ? for each endpoint ? – Royi Namir Oct 17 '19 at 08:32
-2

Haven't used it myself, but I think you might be looking for something like: HttpClientFactoryLite. It forked Microsoft.Extensions.HttpClientFactory and then removed all the "opinionated" parts (e.g., DI, logging, typed clients, etc.) to remove all the external dependencies.

Marc
  • 220
  • 2
  • 3