Do HttpClient and HttpClientHandler have to be disposed between requests?

前端 未结 12 1903
鱼传尺愫
鱼传尺愫 2020-11-22 02:06

System.Net.Http.HttpClient and System.Net.Http.HttpClientHandler in .NET Framework 4.5 implement IDisposable (via System.Net.Http.HttpMessageInvoker).

The usin

相关标签:
12条回答
  • 2020-11-22 02:06

    The general consensus is that you do not (should not) need to dispose of HttpClient.

    Many people who are intimately involved in the way it works have stated this.

    See Darrel Miller's blog post and a related SO post: HttpClient crawling results in memory leak for reference.

    I'd also strongly suggest that you read the HttpClient chapter from Designing Evolvable Web APIs with ASP.NET for context on what is going on under the hood, particularly the "Lifecycle" section quoted here:

    Although HttpClient does indirectly implement the IDisposable interface, the standard usage of HttpClient is not to dispose of it after every request. The HttpClient object is intended to live for as long as your application needs to make HTTP requests. Having an object exist across multiple requests enables a place for setting DefaultRequestHeaders and prevents you from having to re-specify things like CredentialCache and CookieContainer on every request as was necessary with HttpWebRequest.

    Or even open up DotPeek.

    0 讨论(0)
  • 2020-11-22 02:06

    Please take a read on my answer to a very similar question posted below. It should be clear that you should treat HttpClient instances as singletons and re-used across requests.

    What is the overhead of creating a new HttpClient per call in a WebAPI client?

    0 讨论(0)
  • 2020-11-22 02:07

    Using dependency injection in your constructor makes managing the lifetime of your HttpClient easier - taking the lifetime managemant outside of the code that needs it and making it easily changable at a later date.

    My current preference is to create a seperate http client class that inherits from HttpClient once per target endpoint domain and then make it a singleton using dependency injection. public class ExampleHttpClient : HttpClient { ... }

    Then I take a constructor dependency on the custom http client in the service classes where I need access to that API. This solves the lifetime problem and has advantages when it comes to connection pooling.

    You can see a worked example in related answer at https://stackoverflow.com/a/50238944/3140853

    0 讨论(0)
  • 2020-11-22 02:08

    Dispose() calls the code below, which closes the connections opened by the HttpClient instance. The code was created by decompiling with dotPeek.

    HttpClientHandler.cs - Dispose

    ServicePointManager.CloseConnectionGroups(this.connectionGroupName);
    

    If you don't call dispose then ServicePointManager.MaxServicePointIdleTime, which runs by a timer, will close the http connections. The default is 100 seconds.

    ServicePointManager.cs

    internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(ServicePointManager.IdleServicePointTimeoutCallback);
    private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100000);
    
    private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)
    {
      ServicePoint servicePoint = (ServicePoint) context;
      if (Logging.On)
        Logging.PrintInfo(Logging.Web, SR.GetString("net_log_closed_idle", (object) "ServicePoint", (object) servicePoint.GetHashCode()));
      lock (ServicePointManager.s_ServicePointTable)
        ServicePointManager.s_ServicePointTable.Remove((object) servicePoint.LookupString);
      servicePoint.ReleaseAllConnectionGroups();
    }
    

    If you haven't set the idle time to infinite then it appears safe not to call dispose and let the idle connection timer kick-in and close the connections for you, although it would be better for you to call dispose in a using statement if you know you are done with an HttpClient instance and free up the resources faster.

    0 讨论(0)
  • 2020-11-22 02:11

    The current answers are a bit confusing and misleading, and they are missing some important DNS implications. I'll try to summarize where things stand clearly.

    1. Generally speaking most IDisposable objects should ideally be disposed when you are done with them, especially those that own Named/shared OS resources. HttpClient is no exception, since as Darrel Miller points out it allocates cancellation tokens, and request/response bodies can be unmanaged streams.
    2. However, the best practice for HttpClient says you should create one instance and reuse it as much as possible (using its thread-safe members in multi-threaded scenarios). Therefore, in most scenarios you'll never dispose of it simply because you will be needing it all the time.
    3. The problem with re-using the same HttpClient "forever" is that the underlying HTTP connection might remain open against the originally DNS-resolved IP, regardless of DNS changes. This can be an issue in scenarios like blue/green deployment and DNS-based failover. There are various approaches for dealing with this issue, the most reliable one involving the server sending out a Connection:close header after DNS changes take place. Another possibility involves recycling the HttpClient on the client side, either periodically or via some mechanism that learns about the DNS change. See https://github.com/dotnet/corefx/issues/11224 for more information (I suggest reading it carefully before blindly using the code suggested in the linked blog post).
    0 讨论(0)
  • 2020-11-22 02:13

    Short answer: No, the statement in the currently accepted answer is NOT accurate: "The general consensus is that you do not (should not) need to dispose of HttpClient".

    Long answer: BOTH of the following statements are true and achieveable at the same time:

    1. "HttpClient is intended to be instantiated once and re-used throughout the life of an application", quoted from official documentation.
    2. An IDisposable object is supposed/recommended to be disposed.

    And they DO NOT NECESSARILY CONFLICT with each other. It is just a matter of how you organize your code to reuse an HttpClient AND still dispose it properly.

    An even longer answer quoted from my another answer:

    It is not a coincidence to see people in some blog posts blaming how HttpClient 's IDisposable interface makes them tend to use the using (var client = new HttpClient()) {...} pattern and then lead to exhausted socket handler problem.

    I believe that comes down to an unspoken (mis?)conception: "an IDisposable object is expected to be short-lived".

    HOWEVER, while it certainly looks like a short-lived thing when we write code in this style:

    using (var foo = new SomeDisposableObject())
    {
        ...
    }
    

    the official documentation on IDisposable never mentions IDisposable objects have to be short-lived. By definition, IDisposable is merely a mechanism to allow you to release unmanaged resources. Nothing more. In that sense, you are EXPECTED to eventually trigger the disposal, but it does not require you to do so in a short-lived fashion.

    It is therefore your job to properly choose when to trigger the disposal, base on your real object's life cycle requirement. There is nothing stopping you from using an IDisposable in a long-lived way:

    using System;
    namespace HelloWorld
    {
        class Hello
        {
            static void Main()
            {
                Console.WriteLine("Hello World!");
    
                using (var client = new HttpClient())
                {
                    for (...) { ... }  // A really long loop
    
                    // Or you may even somehow start a daemon here
    
                }
    
                // Keep the console window open in debug mode.
                Console.WriteLine("Press any key to exit.");
                Console.ReadKey();
            }
        }
    }
    

    With this new understanding, now we revisit that blog post, we can clearly notice that the "fix" initializes HttpClient once but never dispose it, that is why we can see from its netstat output that, the connection remains at ESTABLISHED state which means it has NOT been properly closed. If it were closed, its state would be in TIME_WAIT instead. In practice, it is not a big deal to leak only one connection open after your entire program ends, and the blog poster still see a performance gain after the fix; but still, it is conceptually incorrect to blame IDisposable and choose to NOT dispose it.

    0 讨论(0)
提交回复
热议问题