docs
docs copied to clipboard
HttpClient is not always a good alternative
HttpClient may be a great option for many scenarios but in a large multi-tenant application that communicates with many 3rd party API's it isn't really an option. I have tried to use it instead of WebRequest but ran into problems.
Using{ HttpClient } blocks caused our production servers to run out of ports quite fast. Turned out that it is not supposed to be disposed... But in that case it is not usable, we need to set the headers and have separate cookies for each and every customer connecting to different 3rd party API's in multiple threads.
I hope this is not an arm twister in order to promote micro service architecture with queues in stead of fast in-process synchronous communication. It's good for fallback when something goes down but immediate processing is much more user-friendly when possible.
If something gets deprecated there better first be a good replacement. Can't the flaws be fixed instead of deprecated?
Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
- ID: 2cef6c88-811c-4651-4e8f-87365a799dda
- Version Independent ID: 3d53a1d2-c6bf-7698-02ed-759679547f2b
- Content: Breaking change: WebRequest, WebClient, and ServicePoint are obsolete - .NET
- Content Source: docs/core/compatibility/networking/6.0/webrequest-deprecated.md
- Product: dotnet-fundamentals
- GitHub Login: @gewarren
- Microsoft Alias: gewarren
Hi @mlsomers It sounds like you're seeing socket exhaustion issues.
As an alternative to using var client = new HttpClient();
, which can lead to socket exhaustion, you should consider using the IHttpClientFactory
.
The original and well-known HttpClient class can be easily used, but in some cases, it isn't being properly used by many developers.
Though this class implements
IDisposable
, declaring and instantiating it within a using statement is not preferred because when the HttpClient object gets disposed of, the underlying socket is not immediately released, which can lead to a socket exhaustion problem. For more information about this issue, see the blog post You're usingHttpClient
wrong and it's destabilizing your software.
Therefore, HttpClient is intended to be instantiated once and reused throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. That issue will result in SocketException errors. 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.
This newish article provides some further discussion about when and how to use HttpClient as well: https://docs.microsoft.com/en-us/dotnet/fundamentals/networking/httpclient-guidelines.
Maybe I gave up on HttpClient to soon, but I'm not convinced yet. The advice of using a singleton will never work in my application, unless I use a lock(syncRoot) and bring our service down to a grinding halt (and make async await useless even if deadlocks can be avoided).
Re-using makes me nervous. I read about the typed client, but that's not good enough. There can be multiple instances of the same class sending messages for different customers that need different headers, cookies, HMAC API keys, etc for the same 3rd party service... How thread-safe can a shared HttpClient be?
@karelz Could you help to answer Louis's questions?
@mlsomers why would you need to lock your singleton? That would be necessary only if you plan to change shared state.
HttpClient is thread safe, unless you try to modify defaults while using it, etc. Once you have an instance, you can run parallel SendAsyncs as much as you want -- the right thing will happen. That means you need to set headers as part of each request.
If you want to have different defaults of headers for convenience, you can have multiple HttpClient (static) instances over the same HttpClientHandler. That is what IHttpClientFactory
does under the hood.
@karelz thank you for your reply. So I should use a static pool of HttpClient
s for a specific service (with default headers), and create HttpRequestMessage
instances with all the additional unique headers for each thread/tenant...
There dos not seem to be a non DI (dependency injection) example but I guess I can figure that out...
The HttpMessageHandler
, as far as I can see this item can be used in a using
block and can be disposed like normal... However I see in the documentation that they are pooled which confuses me. What reason is there to pool HttpMessageHandler
objects? Are they holding any resources other than memory? Actually I think I would not be using these explicitly on a server application very often, maybe these are more targeted for client applications that need to keep track of cookies from multiple hosts, eg. OpenIdConnect SSO clients for example, but then I still cannot find any reason why these would be pooled?
If you can shed some light on the thread-safty of HttpMessageHandler
items (and if they are meant to be used in server-side applications at all), I guess this issue can be closed, even if it is going to be a painful rewrite and redesign of architecture.
I hope the trusty WebRequest doesn't disappear too soon so we get the time to redesign stuff in a timely manner or else get stuck in an older framework.
The HttpMessageHandler
objects are tied to connections and are pooled to prevent socket exhaustion.
I'm a huge advocate for this concept. When you come across articles with titles like "You're Using HttpClient Wrong," it leads me to believe that the issue lies in the design of the implementation itself, rather than user error. As someone who frequently creates multi-tenant applications, I completely get the rationale behind reusing sockets and the appeal of consolidating configurations in the startup file. While I don't think these features should be eliminated, there are countless situations where configurations may need to be adjusted on-the-fly. Ideally, I should be able to set an authentication header and utilize already open sockets for other tenants without having to worry about any interference or contamination.