【问题标题】:How to make a C# application using HttpWebRequest behave like fiddler如何使用 HttpWebRequest 使 C# 应用程序表现得像提琴手
【发布时间】:2011-06-30 17:58:33
【问题描述】:

我有一个控制台应用程序,它使用 20 个左右的线程连接到远程 Web 服务器并发送任意大小相当小的 http 请求,100% 通过 ssl。远程 Web 服务器实际上是一个完整的负载平衡数据中心,其中充满了每秒可以处理数十万个请求的高可用性系统。这不是服务器或带宽问题。话虽如此,我没有运行它,也没有影响它的配置方式,所以即使我想也无法进行服务器端更改。

使用 fiddler 运行应用程序时,应用程序的运行速度非常快。当不在提琴手中运行时,它真的要慢得多,以至于对手头的任务毫无用处。它似乎在进程的早期某个时间点也被锁定,但这可能只是一个死锁问题,我还不确定。

无论如何,fiddler 作为代理,无疑会以某种方式修改我的请求/连接,以确保出色的吞吐量,但我不知道它在做什么。我试图弄清楚,以便我可以强制我的 .net 应用程序模仿提琴手的连接处理行为,而无需实际通过提琴手运行它

我已经把连接代码贴在下面了。

     using System;
     using System.Collections.Generic;
     using System.Linq;
     using System.Text;
     using System.Net;
     using System.IO;

     namespace Redacted
     {
        public class HiveCommunicator
        {

           public static IResponse SendRequest(IRequest request) {

              ServicePointManager.DefaultConnectionLimit = 60;
              ServicePointManager.Expect100Continue = false;


              string hostUrlString = string.Empty;
              if (request.SiteID <= 0)
                 hostUrlString = string.Format("{0}://{1}{2}", request.UseSSL ? "https" : "http", DataCenters.GetCenter(request.DataCenter), request.Path);
              else
                 hostUrlString = string.Format("{0}://{1}{2}", request.UseSSL ? "https" : "http", DataCenters.GetCenter(request.DataCenter), string.Format(request.Path, request.SiteID));

              HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(hostUrlString);

              switch (request.ContentType)
              {
                 default:
                 case ContentTypes.XML:
                    webRequest.ContentType = "application/xml";
                    break;
                 case ContentTypes.JSON:
                    webRequest.ContentType = "application/json";
                    break;
                 case ContentTypes.BINARY:
                    webRequest.ContentType = "application/octet-stream";
                    break;
              }

              if (request.RequiresAuthorizationToken)
              {
                 AuthorizationToken tok = HiveAuthentication.GetToken(request.SiteID);
                 if (tok == null)
                 {
                    return null;
                 }
                 webRequest.Headers.Add(HttpRequestHeader.Authorization, tok.Token);
              }

              bool UsesRequestBody = true;

              switch (request.HttpVerb)
              {
                 case HttpVerbs.POST:
                    webRequest.Method = "POST";
                    break;
                 case HttpVerbs.DELETE:
                    webRequest.Method = "DELETE";
                    UsesRequestBody = false;
                    break;
                 case HttpVerbs.PUT:
                    webRequest.Method = "PUT";
                    break;
                 default:
                 case HttpVerbs.GET:
                    webRequest.Method = "GET";
                    UsesRequestBody = false;
                    break;
              }

              HttpWebResponse webResponse = null;
              Stream webRequestStream = null;

              byte[] webRequestBytes = null;
              if (UsesRequestBody)
              {
                 webRequestBytes = request.RequestBytes;
                 webRequest.ContentLength = webRequestBytes.Length;
                 webRequestStream = webRequest.GetRequestStream();
                 for (int i = 0; i < webRequest.ContentLength; i++)
                 {
                    webRequestStream.WriteByte(webRequestBytes[i]);
                 }
              }

              try
              {
                 webResponse = (HttpWebResponse)webRequest.GetResponse();
              }
              catch (WebException ex)
              {

                 webResponse = (HttpWebResponse)ex.Response;
              }

              if (UsesRequestBody)
              {
                 webRequestStream.Close();
                 webRequestStream.Dispose();
              }

              IResponse respReturn = request.ParseResponse(webResponse);
              webResponse.Close();

              return respReturn;
           }
        }
     }

【问题讨论】:

    标签: c# .net httpwebrequest fiddler


    【解决方案1】:

    我感谢这里试图提供帮助的人们。不幸的是,这需要致电 Microsoft 专业支持。

    尽管我使用的是ServicePointManager.Expect100Continue = false;,但它发生在应用程序生命周期的后期。查看 System.Net.Trace 日志,我们看到仍在使用 expect-100 continue 标头(使用 fiddler 时除外)。解决方案是将其放入应用启动中(在 Main() 中)

    我还试图在关闭请求流之前读取响应流。

    修复后,一切都加快了。该应用程序在没有 fiddler 的情况下运行速度比使用 fiddler 快得多,这是我所期望的。

    有几个人说要在 HttpWebResponse 上调用 dispose。该类没有公共 Dispose 方法。我假设 .Close() 在内部调用 .Dispose() 。

    【讨论】:

    • 我也遇到了这个标题的问题。我一辈子都无法理解为什么 MS 让这件事变得如此难以改变。
    【解决方案2】:

    您可以使用 Fiddler 的“连接选项”来查看 Fiddler 强大吞吐量的原因是否是重用客户端连接。如果是这种情况,您可能需要考虑实现一个共享的安全 http 连接池,或者只是去看电影什么的。 ^^

    【讨论】:

    • 我不确定在哪里可以找到有关设置“共享安全 http 连接池”的信息我想知道您是否可以建议一些谷歌搜索词
    • (stackoverflow.com/questions/4933450/…) Jon Skeet 建议在 WebResponse 上显式调用 dispose,以便底层基础设施可以重用相同的连接。也许这会有所帮助。
    【解决方案3】:

    在这里大胆猜测,但它可能与简单的 app.config 设置有关:

    <system.net>
      <connectionManagement>
        <add address="*" maxconnection="40"/>
      </connectionManagement>
    </system.net>  
    

    我曾经在多线程 HTTP 请求应用程序中遇到过同样的问题,这解决了这个问题。

    【讨论】:

    • 感谢您的提示,我已经尝试过了,但似乎没有用,但感谢您的建议 ;)
    【解决方案4】:

    鉴于您的应用程序发送“任意大小的 http 请求”,它可能有助于禁用 Nagle 算法。

    ServicePointManager.UseNagleAlgorithm = true;
    

    来自MSDN:使用 HttpWebRequest 时,许多元素会影响性能,包括:

    Nagle 算法 [...] 在通过网络发送数据之前,将小消息序列累积到较大的 TCP 数据包中。 [...] 通常,对于恒定的大容量吞吐量,使用 Nagle 算法可以提高性能。但是对于较小吞吐量的应用程序,可能会看到性能下降。 [...] 如果应用程序使用低延迟连接,将这个属性设置为 false 可能会有所帮助。

    【讨论】:

    • 这是导致我出现问题的原因。当我设置 ServicePointManager.UseNagleAlgorithm = false 我的连接从 200 毫秒变为 6 毫秒。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多