-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Warning about incorrect lifecycle policy #2315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -28,6 +28,14 @@ | |||||||
<format type="text/markdown"><![CDATA[ | ||||||||
|
||||||||
## Remarks | ||||||||
|
||||||||
> [!WARNING] | ||||||||
> - <xref:System.Net.Http.HttpClient> is intended to be instantiated once and re-used throughout the life of an application. Instantiating an <xref:System.Net.Http.HttpClient> class for every request will exhaust the number of sockets available under heavy loads and result in a <xref:System.Net.Sockets.SocketException>. However, having long-lived <xref:System.Net.Http.HttpClient> instances can lead to stale DNS usage and non-functional connections. Use one of the following options to avoid these problems: | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem with saying "instantiated once" is that it makes users think they literally should not create more than one of these, which is incorrect. An instance of HttpClient basically corresponds to a connection pool. So you want to reuse the same HttpClient instance because it will reuse connections from that pool. But there are cases where you don't care about sharing/reusing connections, e.g. when you are talking to two different origin servers and thus wouldn't be sharing connections anyway. And if you want to have, e.g. different authentication settings or redirect settings or version policy or whatever, it it totally fine and, in fact, advisable to have a different HttpClient instance for each of these. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @karelz @geoffkizer I'm sorry this fell by the wayside. The PR comments coincided with me getting covid last year, and the recovery/brain fog limited my mental capacity for "a good bit", as we say in the south.
This actually doesn't make total sense to me. Can you explain why?
That's all fine, except that "basically" is vague, too. IHttpClientBuilder defines an explicit lifetime for re-use, here: IHttpClientBuilder also mentions that the HttpMessageHandler is what gets pooled, so perhaps you mean to say "HttpClient provides a connection pool for HttpMessageHandler instances." Here is another area that likely need improving: ITypedHttpClientFactory confusingly doesn't use DI but relies on a static cache - and thus should not be confused with IHttpClientFactoryThis is a really bizarre remark to me. Just reading it sounded incorrect. It reads to the cautionary reader that there is a lifetime scope mismatch, when you think about it! So, I decided to dig deeper and found THIS which confirmed my worry - it's a cache that has no fault tolerance mechanisms and is basically a memory leak: ITypedHttpClientFactory has a reference to an undefined variable.
HttpClientFactoryExtensions - IHttpClientFactory has a "default configuration" 👀 ?This also doesn't read well - how can an interface have a "default configuration" 👀 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Not sure what is not clear. It is statement. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me rephrase: If the article Use IHttpClientFactory to implement resilient HTTP requests is correct guidance, and not using HttpClientFactory leads to non-resilient HTTP Requests, why would DI vs. non-DI be the controlling factor for its use? I think saying it is only suited for DI environments is rather confusing in light of the problems highlighted with HttpClient. But perhaps HttpClient has been refactored in newer versions of .NET 5/6 and this no longer applies? See specifically this part of the article which addresses HttpClient shortcomings: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, I think I see the problem, further down in that same article I linked, there is a caution about HttpClientFactory being somewhat tightly coupled to Microsoft.Extensions.DependencyInjection nuget package, with a GitHub discussion linked for more info: dotnet/aspnetcore#28385 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think, if you scroll down to the bottom of the aforementioned GitHub discussion, you will see a comment by an end user that is fairly lucid (unfortunately, Eric Sampson then decided to close the thread because I guess it was too flamboyant): dotnet/aspnetcore#28385 (comment) In particular, "Example 4: You're creating a redistributable web-service client library class:" points out that "Figuring out how to handle HttpClient when you don't own the entrypoint or host program that will run your code is very difficult." Separtely, Eric's reply - go look at the Azure SDK for .NET - is not good. Not only was there no direct link to the code, but Guess what, I followed his suggestion and found production code commented out in ServiceClient.cs - how is this the example Microsoft wants us to follow? :) and a TODO ...and, it looks like the actual pattern for using ServiceClient.cs is to rely on some code generation, and I can't seem to figure out where that code generator lives, but I can see the checked-in artifacts:
Point is: The "if you want to see how someone has handled these issues, you can take a look at the Azure SDK. IIRC they've dealt with this" guidance is not good. I think this is what Joshua Bloch would call the empathy gene importance in good API design. https://www.youtube.com/watch?v=aAb7hSCtvGw There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I filed a ticket with the Azure SDK team and they elaborated on what their solution to Jehoel's Scenario 4 is, where you are a API author providing API services to a client. See: Azure/azure-sdk-for-net#29035 (comment) |
||||||||
> | ||||||||
> - Use static <xref:System.Net.Http.HttpClient> instances, which are recycled on a regular basis. | ||||||||
> - Use <xref:System.Net.Http.HttpClientFactoryExtensions.CreateClient(IHttpClientFactory)> to create the <xref:System.Net.Http.HttpClient>. | ||||||||
> - On .NET Core and .NET 5.0 and later, use static instances and set <xref:System.Net.Http.SocketsHttpHandler.PooledConnectionLifetime>. | ||||||||
Comment on lines
+36
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
||||||||
The <xref:System.Net.Http.HttpClient> class instance acts as a session to send HTTP requests. An <xref:System.Net.Http.HttpClient> instance is a collection of settings applied to all requests executed by that instance. In addition, every <xref:System.Net.Http.HttpClient> instance uses its own connection pool, isolating its requests from requests executed by other <xref:System.Net.Http.HttpClient> instances. | ||||||||
|
||||||||
The <xref:System.Net.Http.HttpClient> also acts as a base class for more specific HTTP clients. An example would be a FacebookHttpClient providing additional methods specific to a Facebook web service (a GetFriends method, for instance). Derived classes should not override the virtual methods on the class. Instead, use a constructor overload that accepts <xref:System.Net.Http.HttpMessageHandler> to configure any pre- or post-request processing instead. | ||||||||
|
@@ -56,7 +64,7 @@ | |||||||
|
||||||||
9. <xref:System.Net.Http.HttpClient.SendAsync%2A> | ||||||||
|
||||||||
<xref:System.Net.Http.HttpClient> is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors. Below is an example using HttpClient correctly. | ||||||||
The following code snippet shows how to use <xref:System.Net.Http.HttpClient>: | ||||||||
|
||||||||
```csharp | ||||||||
public class GoodController : ApiController | ||||||||
|
@@ -2152,4 +2160,4 @@ public class GoodController : ApiController | |||||||
</Docs> | ||||||||
</Member> | ||||||||
</Members> | ||||||||
</Type> | ||||||||
</Type> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.