HttpClient and Usign Statement

469 views
Skip to first unread message

Preguntón Cojonero Cabrón

unread,
Feb 19, 2018, 4:03:15 PM2/19/18
to AltNet-Hispano
Hola gente!!!

Me han hecho llegar también este caso: el mal uso de HttpClient y Using.

Alguna experiencia al respecto?




The using statement is a C# nicity for dealing with disposable objects. Once the using block is complete then the disposable object, in this case HttpClient, goes out of scope and is disposed. The dispose method is called and whatever resources are in use are cleaned up. This is a very typical pattern in .NET and we use it for everything from database connections to stream writers. Really any object which has external resources that must be clean up uses the IDisposable interface.

And you can’t be blamed for wanting to wrap it with the using. First of all, it’s considered good practice to do so. In fact, the official docs for usingstate:

As a rule, when you use an IDisposable object, you should declare and instantiate it in a using statement.

Secondly, all code you may have seen since…the inception of HttpClient would have told you to use a using statement block, including recent docs on the ASP.NET site itself. The internet is generally in agreement as well.

But HttpClient is different. Although it implements the IDisposable interface it is actually a shared object. This means that under the covers it is reentrant) and thread safe. Instead of creating a new instance of HttpClient for each execution you should share a single instance of HttpClientfor the entire lifetime of the application






The Fix is In

I really must thank Harald S. Ulrksen and Darrel Miller for pointing me to The Patterns and Practices documents on this.

If we share a single instance of HttpClient then we can reduce the waste of sockets by reusing them:

using System;
using System.Net.Http;

namespace ConsoleApplication
{
public class Program
{
private static HttpClient Client = new HttpClient();
public static async Task Main(string[] args)
{
Console.WriteLine("Starting connections");
for(int i = 0; i<10; i++)
{
var result = await Client.GetAsync("http://aspnetmonsters.com");
Console.WriteLine(result.StatusCode);
}
Console.WriteLine("Connections done");
Console.ReadLine();
}
}
}

Note here that we have just one instance of HttpClient shared for the entire application. Eveything still works like it use to (actually a little faster due to socket reuse).


If you have any kind of load at all you need to remember these two things:

  1. Make your HttpClient static.
  2. Do not dispose of or wrap your HttpClient in a using unless you explicitly are looking for a particular behaviour (such as causing your services to fail).

Wrapping Up

The socket exhaustion problems we had been struggling with for months disapeared and our client threw a virtual parade. I cannot understate how unobvious this bug was. For years we have been conditioned to dispose of objects that implement IDisposable and many refactoring tools like R# and CodeRush actually warn if you don’t. In this case disposing of HttpClient was the wrong thing to do. It is unfortunate that HttpClientimplements IDisposable and encourages the wrong behaviour


Algún comentario al utilizar una instancia static y safe-Thread:


According to MSDN only the public static methods of HttpClient are thread-safe. So suggesting to make the HttpClient instance static for any application will lead to new issues, depending on thread usage.


Besides the thread-safe issue, the underlying socket's TCP circuit will have session with a single remote machine in load balanced scenario and it will always hit the same box which defeats the purpose of load balancing.


It's not thread-safe and can lead to deadlocks when used in high-load environments. 
The solution I ended up creating a special kind of pool for HttpClient objects:
http://pastebin.com/jftEbWrc


You just provide your HttpClient factory and dispose methods and the LimitedPool does the rest:

_httpClientPool = new LimitedPool<httpclient>(
CreateHttpClient, client => client.Dispose(), HttpClientLifetime);

using (var httpClientContainer = _httpClientPool.Get())
{ ... use httpClientContainer.Value ... }

 

When httpClientContainer is disposed, the HttpClient is actually returned back to the pool for other threads to use. When lifetime is reached next dispose will eventually call the Dispose method.



, I have updated LimitedPool class: https://pastebin.com/9ypJcw9X
It can now automatically dispose unused items even if they hadn't been accessed for some time. We've been using it in production for more than a year now (~1 billion requests per week), no problems so far.



Muchas referencias al respecto...


https://stackoverflow.com/questions/37928543/httpclient-single-instance-with-different-authentication-headers


https://blogs.msdn.microsoft.com/alazarev/2017/12/29/disposable-finalizers-and-httpclient/


http://faithlife.codes/blog/2017/03/usage-guidelines-for-httpclient/

https://ankitvijay.net/2016/09/25/dispose-httpclient-or-have-a-static-instance/


https://softwareengineering.stackexchange.com/questions/330364/correct-way-of-using-httpclient


This post states:

An HttpClient instance is a collection of settings applied to all requests executed by that instance. In addition, every HttpClient instance uses its own connection pool, isolating its requests from requests executed by other HttpClient instances.





Cual sería el mejor FIX para ello?


 
Reply all
Reply to author
Forward
0 new messages