【问题标题】:Which type of singleton pattern should be used for creating HTTP client for my web application应该使用哪种类型的单例模式为我的 Web 应用程序创建 HTTP 客户端
【发布时间】:2015-11-10 12:59:14
【问题描述】:

我有一个网络应用程序。我发现性能瓶颈可能是我为每个请求一次又一次地创建 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://csharpindepth.com/Articles/General/Singleton.aspx

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

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

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

【问题讨论】:

  • 您说瓶颈“可能是” HttpClient 的创建——您是否进行了测试以确保是这种情况?
  • 我严重怀疑你仅仅因为更新HttpClient而存在性能瓶颈。如果您,则机器严重不足。尝试在请求之间共享HttpClient 实例不仅是一个坏主意,而且还是一种微优化。如果您遇到性能问题,请寻找真正的来修复。
  • 是的,它不应该是唯一造成问题的东西。但它是一个,可能是一个次要的。我仍在进行负载测试。您可以阅读:stackoverflow.com/questions/22560971/…

标签: asp.net asp.net-mvc-4 singleton httpclient web-optimization


【解决方案1】:

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

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

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];
    }

请注意,由于 HttpClient 实现了 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 实例执行的请求隔离开来。

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

【讨论】:

  • 有趣的是,在 HttpClient 文档页面的不同修订版中 -- msdn.microsoft.com/en-us/library/… -- Remarks 部分有一个线程安全的方法列表。
  • 我只需要使用 HttpClient.GetStringAsync 或 HttpClient.GetAsync。由于它们是线程安全的,我假设我可以使用单个实例,因为标题等是相同的。你能告诉它是否应该延迟加载吗?如果我错了,请纠正我。
  • 延迟加载通过将初始化推迟到对象被使用的那一刻来优化重对象的初始化(当它不被使用时它不会被初始化)。如果是单例,通常没关系。
猜你喜欢
  • 1970-01-01
  • 2020-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-17
  • 1970-01-01
相关资源
最近更新 更多