0

我有一个网络应用程序。我发现性能瓶颈可能是我为每个请求一次又一次地创建 Http 客户端。

public static class DemoHttpClient
    {
       public static HttpClient GetClient()
       {
           HttpClient client = new HttpClient();
           client.BaseAddress = new Uri(DemoConstants.DemoAPI);
           client.DefaultRequestHeaders.Accept.Clear();
           client.DefaultRequestHeaders.Accept.Add(
                new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

           return client;
}
    }

public class DemoConstants
{
    public const string DemoAPI = "http://localhost/";
}

我计划为此实施单例。并发现这篇非常有用的文章。 http://cshapindepth.com/Articles/General/Singleton.aspx

我对在服务器上部署 ASP.NET MVC Web 应用程序生命周期的准确程度感到困惑。假设将有多个线程调用相同的资源,该资源一次又一次地创建新的 http 客户端..

我们应该在这里做什么.. 1) 延迟加载 HTTP 客户端?2)不懒加载吗?

我们应该使用哪种特定方法?

4

1 回答 1

0

这听起来不是个好主意。特别是,看看HttpClient课程的文档:

此类型的任何公共静态(在 Visual Basic 中为 Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。

https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.118%29.aspx

这意味着从多个线程访问同一个单例实例将导致未定义的问题。

但是,您可以做的是,您可以在单个请求中重用相同的实例。这可以通过在Items容器中存储一个实例来完成:

   private static string ITEMSKEY = "____hclient";

   public static HttpClient GetClient()
   {
       if ( HttpContext.Current.Items[ITEMSKEY] == null )
       {
          HttpClient client = new HttpClient();
          client.BaseAddress = new Uri(DemoConstants.DemoAPI);
          client.DefaultRequestHeaders.Accept.Clear();
          client.DefaultRequestHeaders.Accept.Add(
            new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

          HttpContext.Current.Items.Add( ITEMSKEY, client );
       }

       return (HttpClient)HttpContext.Current.Items[ITEMSKEY];
    }

请注意,由于HttpClientimplements IDisposable,将此类实例放置在管道中的某处仍然是一个好主意,例如在EndRequest应用程序管道的事件中。

更新:正如@LukeH 的评论中所指出的,.NET 4.5 和 4.6 文档的更新版本指出HttpClient该类的某些方法线程安全的:

https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.110%29.aspx

更新后的备注部分指出,单个实例基本上是应用于此实例执行的所有请求的共享设置的集合。然后,文档说:

此外,每个 HttpClient 实例都使用自己的连接池,将其请求与其他 HttpClient 实例执行的请求隔离开来。

这意味着不同池的隔离仍然有意义,我个人的建议仍然是不要有单例,因为您可能仍然需要在连续请求之间更改一些设置。

于 2015-11-10T13:07:40.697 回答