☁️Properly Using HttpClient with C# in .NET Standard 2.0
The correct and proper way to use HttpClient with C# in .NET Standard 2.0 to avoid both socket exhaustion and DNS updates.
If you are not yet aware of the issues with HttpClient in .NET here's a brief summary:
HttpClient
implementsIDisposable
, therefore, intuitively 🤷♂️, it should be disposed after usage.- Disposing it leads to socket exhaustion. That's because the internal http handler uses a connection from a connection pool that doesn't dispose along with the HttpClient.
- Not Disposing, obviously, leads to memory issues.
- Not Disposing + single instance of HttpClient (static readonly new HttpClient) works, and it's the quickest fix, BUT it misses any DNS updates that happen after instantiation.
Here's the clean fix, in .NET Standard 2.0
Install the StandardSocketsHttpHandler
NuGet. In .netcore2.1+ this is not needed as the framework itself has this implementation embedded (SocketsHttpHandler).
dotnet add package StandardSocketsHttpHandler --version 2.2.0.4
Instantiate your HttpClient
as follows:
static HttpClient BuildNewHttpClient()
{
return
new HttpClient
(
handler: BuildNewStandardSocketsHttpHandler(),
disposeHandler: true
);
}
static StandardSocketsHttpHandler BuildNewStandardSocketsHttpHandler()
{
return
new StandardSocketsHttpHandler()
{
// The maximum idle time for a connection in the pool. When there is no request in
// the provided delay, the connection is released.
// Default value in .NET 6: 1 minute
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1),
// This property defines maximal connection lifetime in the pool regardless
// of whether the connection is idle or active. The connection is reestablished
// periodically to reflect the DNS or other network changes.
// ⚠️ Default value in .NET 6: never
// Set a timeout to reflect the DNS or other network changes
PooledConnectionLifetime = TimeSpan.FromHours(.5),
};
}
Safely use it as IDisposable
using (HttpClient http = BuildNewHttpClient())
{
[...]
}
I don't own this solution, I found the original idea here: https://www.meziantou.net/avoid-dns-issues-with-httpclient-in-dotnet.htm
Another fix, that I personally don't like that much is fiddling with ServicePointManager
and ConnectionLeaseTimeout
var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab"));
sp.ConnectionLeaseTimeout = 60*1000; // 1 minute
as described here: http://byterot.blogspot.com/2016/07/singleton-httpclient-dns.html
I personally haven't even tried this workaround, but I guess it works... 🤷♂️