在WebAPI客户机中每次调用创建一个新的HttpClient的开销是多少?

WebAPI客户端的HttpClient生存期应该是多少?对于多个调用,使用一个HttpClient实例更好吗?创建和处理每个请求的http client的开销是多少,如下例所示(摘自http://www.asp.net/web api/overview/webapi client s/calling-a-web-api-from-a-net-client):

using (var client = new HttpClient())

{

    client.BaseAddress = new Uri("http://localhost:9000/");

    client.DefaultRequestHeaders.Accept.Clear();

    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));



    // New code:

    HttpResponseMessage response = await client.GetAsync("api/products/1");

    if (response.IsSuccessStatusCode)

    {

        Product product = await response.Content.ReadAsAsync<Product>();

        Console.WriteLine("{0}\\t${1}\\t{2}", product.Name, product.Price, product.Category);

    }

}

6个回答

  1. HttpClient被设计为可用于多个调用。即使跨多个线程,HttpClientHandler也有凭据和cookie,可以在调用之间重用。拥有一个新的HttpClient实例需要重新设置所有这些内容。此外,DefaultRequestHeaders属性包含用于多个调用的属性。必须在每个请求上重置这些值会使这一点失败。HttpClient的另一个主要优点是能够将HttpMessageHandlers添加到请求/响应管道中以应用横切关注点。它们可以用于日志记录、审核、限制、重定向处理、脱机处理、捕获度量。各种各样的事情。如果在每个请求上创建一个新的HttpClient,则需要在每个请求上设置所有这些消息处理程序,并且还需要以某种方式提供在这些处理程序的请求之间共享的任何应用程序级状态。您越多地使用HtpCclipse的特性,就越多地看到重用现有实例是有意义的。然而,我认为最大的问题是,当HTTPcli客户类被配置时,它处理HTTPclieNeNDELL,然后强制关闭由Service PositMeor管理的连接池中的TCP/IP连接。这意味着每一个带有新HttpClient的请求都需要重新建立一个新的TCP/IP连接。从我的测试来看,在局域网上使用纯HTTP,性能影响可以忽略不计。我怀疑这是因为有一个底层的TCP keepalive正在保持连接打开,即使HttpClientHandler试图关闭它。在网上的请求,我看到了一个不同的故事。由于每次都必须重新打开请求,我看到40%的性能受到影响。我怀疑HTTPS连接上的影响会更严重。我的建议是在应用程序的生命周期中为连接到的每个不同API保留一个HttpClient实例。

  2. 如果你想扩大你的应用程序,差别是巨大的!根据负载的不同,您将看到非常不同的性能数字。正如Darrel Miller所提到的,HttpClient被设计为在请求之间重用。这一点得到了BCL团队的人的证实。我最近的一个项目是帮助一个非常大和知名的在线计算机零售商扩大黑色星期五/假日流量为一些新系统。我们在使用HttpClient时遇到了一些性能问题。因为它实现了IDisposable,所以开发人员通过创建一个实例并将其放在using()语句中来完成您通常要做的事情。一旦我们开始进行负载测试,应用程序就会让服务器崩溃——是的,服务器不仅仅是应用程序。原因是HttpClient的每个实例都在服务器上打开一个端口。由于GC的不确定性终结以及您正在使用跨多个OSI层的计算机资源这一事实,关闭网络端口可能需要一段时间。事实上,Windows操作系统本身可能需要20秒来关闭一个端口(每个微软)。我们打开端口的速度比关闭服务器端口的速度快,这将CPU的消耗率降低到100%。我的修复方法是将HttpClient更改为解决问题的静态实例。是的,它是一个可丢弃的资源,但是性能上的差异大大超过了任何开销。我建议您做一些负载测试,看看您的应用程序是如何运行的。您也可以查看web api指导页,以获取文档和示例,网址是https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client请特别注意:HttpClient打算实例化一次,并在整个生命周期中重复使用申请的。特别是在服务器应用程序中,为每个请求创建一个新的HttpClient实例将耗尽重载下可用的套接字数量。这将导致SocketException错误。如果您发现需要使用具有不同头、基地址等的静态HttpClient,则需要手动创建HttpRequestMessage并在HttpRequestMessage上设置这些值。然后,使用HttpClient:sendaync(HttpRequestMessage requestMessage,…)

  3. 作为其他应答状态,HttpClient意味着重用。但是,跨多线程应用程序重用单个HttpClient实例意味着您不能更改其状态属性的值,类似于BaseAddress和DefaultRequestHeaders(因此,只有当它们在应用程序中是常量时,才能使用它们)。绕过此限制的一种方法是用一个类包装HttpClient,该类复制您需要的所有HttpClient方法(GetAsync、PostAsync等),并将它们委托给一个单一的HttpClient。不过,这相当乏味(您还需要包装扩展方法),幸运的是还有另一种方法-继续创建新的HttpClient实例,但重用底层的HttpClientHandler。确保你没有丢弃处理器:

    HttpClientHandler _sharedHandler = new HttpClientHandler(); //never dispose this
    
    HttpClient GetClient(string token)
    
    {
    
        //client code can dispose these HttpClient instances
    
        return new HttpClient(_sharedHandler, disposeHandler: false)         
    
        {
    
           DefaultRequestHeaders = 
    
           {
    
                Authorization = new AuthenticationHeaderValue("Bearer", token) 
    
           } 
    
        };
    
    }
    
    

  4. 与大容量网站相关,但不直接与HttpClient相关。我们在所有服务中都有下面的代码片段。

            // number of milliseconds after which an active System.Net.ServicePoint connection is closed.
    
            const int DefaultConnectionLeaseTimeout = 60000;
    
    
    
            ServicePoint sp =
    
                    ServicePointManager.FindServicePoint(new Uri("http://<yourServiceUrlHere>"));
    
            sp.ConnectionLeaseTimeout = DefaultConnectionLeaseTimeout;
    
    

    来自https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Net.ServicePoint.ConnectionLeaseTimeout);k(TargetFrameworkMoniker-.NETFramework,版本%3Dv4.5.2);k(DevLang csharp)&rd=true“可以使用此属性确保服务点对象的活动连接不会无限期地保持打开状态。此属性适用于应定期删除和重新建立连接的方案,例如负载平衡方案。默认情况下,当请求的KeepAlive为true时,MaxIdleTime属性设置由于不活动而关闭ServicePoint连接的超时时间。如果ServicePoint具有活动连接,则MaxIdleTime无效,并且连接将无限期保持打开状态。当ConnectionLeaseTimeout属性设置为-1以外的值并且在指定的时间过后,通过在该请求中将KeepAlive设置为false,在为请求提供服务后将关闭活动的ServicePoint连接。设置此值将影响ServicePoint对象管理的所有连接。“当您在CDN或其他终结点后面有要故障转移的服务时,此设置将帮助调用方跟踪您到新的目标。在本例中,故障转移60秒后,所有调用方都应重新连接到新端点。它确实要求您知道依赖服务(您调用的那些服务)及其端点。

  5. 你也可以参考Simon Timms的博客:https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/但是HttpClient是不同的。尽管它实现了IDisposable接口,但它实际上是一个共享对象。这意味着在封面下它是可重入的)并且是线程安全的。与其为每次执行创建一个新的HttpClient实例,还不如在应用程序的整个生命周期中共享一个HttpClient实例。让我们看看原因。

  6. 有一点需要指出的是,没有一个“不使用”博客注意到,你不仅仅需要考虑基础地址和默认地址。一旦将HttpClient设为静态,就会有内部状态在请求之间传递。例如:您正在使用HttpClient向第三方进行身份验证以获取FedAuth令牌(忽略为什么不使用OAuth/OWIN/etc),该响应消息具有FedAuth的Set Cookie头,这将添加到HttpClient状态。下一个登录到您的API的用户将发送最后一个人的FedAuth cookie,除非您在每次请求时都管理这些cookie。

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>