Reputation: 155558
I'm sure we've all read this article that made waves back in 2016: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
For those who haven't, in summary (emphasis mine):
Instead of creating a new instance of
HttpClient
for each execution you should share a single instance of HttpClient for the entire lifetime of the application.
Now, consider an ASP.NET Core application that consumes other web-services by using a HttpClient
. Its usage of HttpClient
falls into two general situations:
An outgoing request that is unauthenticated or uses the website's own credentials - in which case a single HttpClient
really can be shared by all parts of the program.
An outgoing request made on behalf of one of the website's current visitors - or a request that's otherwise using request-specific credentials.
HttpClient
instance can be used, you must be careful not to mutate its state, for example, by setting DefaultRequestHeaders
(e.g. by using the SetBearerToken
extension method).Practically all of the guidance for using HttpClient
in ASP.NET Core says to:
HttpClient
instance as a constructor parameter, and any other DI services).services.AddHttpClient<TClient>()
.
TClient
as a transient instance.ITypedHttpClientFactory<TClient>
as a transient instance.IHttpClientFactory
as a singletonIHttpMessageHandlerFactory
as a singletonWith that now discussed, I'll bring your attention towards a contradiction:
HttpClient
instances must be long-life'd.HttpClient
DI system really makes sure that HttpClient
instances are short-life'd.However, might this be okay if the blog article should really be talking about the underlying HttpMessageHandler
(which is the real HttpClient implementation which is simply wrapped by the thin shell class HttpClient
) - instead of the outer class HttpClient
?
Confounding things, people report problems with using long-life'd HttpClient
instances too and introduce other workarounds which all involve the static class ServicePointManager
which makes me uncomfortable:
ServicePointManager
during application startup.ConnectionLeaseTimeout
ServicePointManager.FindServicePoint()
for every URI you call! That doesn't seem right to me.My questions:
HttpClient
instances really meant to be long-life'd or short-life'd?HttpMessageHandler
instances that need to be long-life'd?HttpClient
or HttpMessageHandler
instances are meant to be long-life'd:
ServicePointManager
issues brought up in both linked blog posts?HttpClient.DefaultRequestHeaders
and instead explicitly set the Authorization
(Cookies or Credentials) headers on each HttpRequestMessage
? Is there another way which has less hassle?DelegatingHandler
? What should be the DI registration of this proposed handler then?If HttpClient
instance (but not HttpMessageHandler
instances) are meant to be short-life'd:
HttpClient.DefaultRequestHeaders
(and SetBearerToken()
)?HttpMessageHandler
instances then?HttpClient.DefaultRequestHeaders
(and SetBearerToken()
)?If HttpClient
instance and HttpMessageHandler
instances are meant to be short-life'd:
IHttpClientFactory
do anything to mitigate those problems?Upvotes: 4
Views: 1117
Reputation: 1187
The underlying problem is with socket connections. new HttpClient()
makes a new port for every request and leaves them open in the TIME_WAIT status:
This isn't the right state for them to sit in, and it takes up a socket without being reused. When the server runs out of sockets, .net throws a SocketException
because it can't make a connection without an open socket. This is also called port exhaustion because the port runs out of sockets.
IHttpClientFactory keeps ports in the ESTABLISHED status, reuses them if another request comes in, and if no request comes in for 4 minutes, it closes them.
Your questions are all ultimately about HTTP and socket connections, but you're using .net classes to describe it. IHttpClientFactory manages the tcp socket connections with a pool of HttpClientHandlers so you don't have to. The answer to the rest of your questions are here: https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
Handlers will be reused by the factory when it needs to create new HttpClients. You can still set dynamic values like the bearer token through your own implementation, e.g. write your own Get<T>
method that takes in a string bearerToken
and then sets the Authorization header so you can keep that code in one place.
... I'll bring your attention towards a contradiction:
The famous blog article - and now Microsoft's own documentation - says the HttpClient instances must be long-life'd. ASP.NET Core's HttpClient DI system really makes sure that HttpClient instances are short-life'd.
Where do you see Microsoft's documentation about IHttpClientFactory that says httpclients need to live long? Through the factory, they're scoped in DI, which means they're disposed at the end of the request. That's why it's safe to reuse the handler, but modify the client at runtime.
Upvotes: 2