【问题标题】:.NET HttpWebRequest Speed versus Browser.NET HttpWebRequest 速度与浏览器
【发布时间】:2011-01-04 03:21:29
【问题描述】:

我对 .Net HttpWebRequest 客户端(或 WebClient,给出类似结果)的性能有疑问。

如果我使用 HttpWebRequest 请求一个 html 页面(在本例中为 news.bbc.co.uk)并分析应用程序读取响应的速度(使用 HttpAnalyzer),这比浏览器( Firefox、Chrome、IE)请求相同的资源(清除所有缓存等)。 .Net 应用程序大约需要 1.7 秒,而浏览器需要 0.2 - 0.3 秒。

这完全取决于代码/应用程序的速度和效率,还是还有其他因素需要考虑?

代码如下:

HttpWebRequest request = null;

Uri uriTest = new Uri("http://news.bbc.co.uk");

request = (HttpWebRequest)WebRequest.Create(uriTest);

request.Method = "GET";
request.KeepAlive = true;
request.Headers["Accept-Encoding"] = "gzip, deflate";

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

response.Close();

【问题讨论】:

  • 使用IP地址代替URL,让你看看DNS服务器是否是延迟的来源。
  • 对不起,我应该提到,已经尝试过(将上面的“news.bbc.co.uk”替换为“212.58.226.77”)没有区别。

标签: c# .net httpwebrequest performance


【解决方案1】:

我会在中间插入 Fiddler,依次运行浏览器请求和 .NET 请求,并确保您确实得到了您的想法。可能发生了重定向或其他一些不正常的事情(也许浏览器在 .NET 等待重新分配时预先附加了“/”等),这些都不是立即可见的。我已经在 .NET HTTP 客户端上构建了巨大的应用程序,但与您描述的完全不同——一定是发生了其他事情。

如果你在 URL 的末尾加上“/”会发生什么?

【讨论】:

  • +1,Fiddler 会告诉你正在传输什么,如果它是 gzip'd 等等。
  • Fiddler 是用 .NET 创建的
【解决方案2】:

如果您提出两个请求,第二个请求会更快发生吗?

我还注意到浏览器与 WebClient 或 WebRequest 之间的速度差异。甚至响应的原始速度也可能完全不同 - 但并非总是如此!

这可能是由以下几种原因引起的:

  • 这可能是所有发生的 .Net 引导。 .Net 程序集在使用之前不会被加载和 JIT 处理,因此即使应用程序本身已经运行了很长时间,在初始调用一段代码时您也会看到速度显着下降。好的 - 所以 .Net 框架本身是 nGen 的 - 但在您的代码和 .Net 框架之间仍然有桥梁可以即时构建。

  • 只需检查您是否在未连接调试器的情况下运行,并且您肯定没有打开符号服务器 - 符号服务器和 VS 在下载符号时会中断程序,从而减慢它们的加载速度。对不起,如果这是一种侮辱;)

  • 浏览器被编码为仅有效使用少数底层套接字;一旦浏览器出现,它们就会被打开并准备好。相比之下,使用 .Net WebClient/WebRequest 的“我们的”代码完全低效,因为每次都重新初始化。

  • 有很多与网络相关的平台资源,虽然 .Net 使使用网络编码变得更加容易,但它仍然存在相同的平台资源问题。因此,您离平台越近,某些代码就会越快。 IE 和 Firefox 等是原生的,因此可以原生地处理系统资源; .Net 不是,因此需要一些编组(=慢)来设置。显然,一旦端口被打开并被使用,.Net 仍然没有懈怠;但它几乎永远不会像编写良好的非编组本机代码那样快。

【讨论】:

    【解决方案3】:

    使用 Ctrl+F5 而不是 F5(调试模式)运行应用程序。你会看到不同:

    class Program
    {
        static void Main()
        {
            using (var client = new WebClient())
            {
                Stopwatch watch = Stopwatch.StartNew();
                var data = client.DownloadData("http://news.bbc.co.uk");
                watch.Start();
                Console.WriteLine("{0} ms", watch.ElapsedMilliseconds);
            }
        }
    }
    

    在我的电脑上打印 880 毫秒。

    【讨论】:

      【解决方案4】:

      那 1.7 秒的故障是什么?我怀疑你正在测量整个过程?

      使用这段代码,我平均得到大约 200 毫秒:

      var request = (HttpWebRequest)WebRequest.Create("http://www.bbc.co.uk/news/");
      
      var stopwatch = new Stopwatch();
      stopwatch.Start();
      
      using (var response = (HttpWebResponse)request.GetResponse())
      {
          stopwatch.Stop();
          Console.WriteLine("Elapsed: {0}ms", stopwatch.ElapsedMilliseconds);
      
          var responseStream = response.GetResponseStream();
          if (responseStream != null)
              using (var sr = new StreamReader(responseStream))
                  Console.WriteLine("Title: {0}", Regex.Match(sr.ReadToEnd(), @"title>(.*)</title").Groups[1].Value);
      }
      

      编辑更改代码只是为了测量实际 HTTP 请求并再次使用 Fiddler 尝试:

      上面的程序:经过:78ms

      提琴手: 总耗时:00:00:00.0620000

      【讨论】:

        【解决方案5】:

        第一次请求页面时,.net 会尝试检测代理设置。解决方案是传入一个空的 WebProxy 对象。这样它只是连接到远程服务器而不是自动检测代理服务器。

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriTest);
        request.Proxy = new WebProxy();
        

        【讨论】:

          【解决方案6】:

          您在使用浏览器时是否观看过网络?也许浏览器正在使用缓存资源?

          【讨论】:

          • 我已经确认浏览器没有使用缓存资源。
          • 我看到了你问题的那一部分。我的意思是相反的——你确认实际上是什么?比较两个场景之间的那个
          • 另外,您只计算到响应。你应该得到response.GetResponseStream() 并阅读全文。
          【解决方案7】:

          Markos 的回答对我来说非常适合同样的问题:

          request.Proxy = new WebProxy();
          

          将 16 秒的请求缩短到不到一秒。谢谢!

          【讨论】:

            【解决方案8】:

            也许 bbc.co.uk 会检查传递给它的 User-Agent header 并根据它处理响应。因此,如果它看到自动化客户端,那么它会缓慢响应,如果它认为线路末端有一个真实的人,那么它就会加快速度。如果您真的想尝试一下,只需告诉 HttpWebRequest 传递不同的标头即可。

            【讨论】:

              【解决方案9】:

              无论何时衡量任何事物,都必须考虑启动成本。如果您的 .net 代码在单个进程中,并且您只测量单个请求,那么您的测量将受到初始化程序集、类型等的首次成本的影响。

              正如 Darin 和其他人所建议的,您应该确保:

              1) 您没有在调试器下运行该进程。 2) 您考虑启动成本。

              您可以执行 #2 的一种方法是发出两个请求并仅测量第二个请求。或者您可以发出 N 个请求,丢弃第一个请求,然后获取最后 N-1 个请求的平均值。还要确保您阅读了实体流。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-04-17
                • 2014-04-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-10-12
                相关资源
                最近更新 更多