如何使用HttpWebRequest使C#应用程序表现得像fiddler

本文关键字:程序表 fiddler 应用程序 应用 何使用 HttpWebRequest | 更新日期: 2023-09-27 18:00:43

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

当使用fiddler运行应用程序时,该应用程序的运行速度惊人。当不在fiddler中运行时,它真的慢得多,以至于对手头的任务毫无用处。在这个过程的早期,它似乎也会在某个时候锁定,但这可能只是一个僵局问题,我还不确定。

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

我已经粘贴了下面的连接代码。

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

如何使用HttpWebRequest使C#应用程序表现得像fiddler

我感谢这里那些尽力提供帮助的人。不幸的是,这需要致电Microsoft专业支持。

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

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

修好后,一切都很快。这个应用程序运行起来比不用麻烦要快得多,这正是我所期望的。

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

您可以试试Fiddler的"连接选项",看看Fiddler强大吞吐量的原因是否是重用客户端连接。如果是这样的话,你可能想考虑实现一个共享的安全http连接池,或者只是去看电影之类的。^^

在这里进行粗略猜测,但它可能与一个简单的app.config设置有关:

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

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

考虑到您的应用程序发送"大小相当小的任意http请求",禁用Nagle算法可能会有所帮助

ServicePointManager.UseNagleAlgorithm = true;

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

  • ServicePointManager类
  • DefaultConnectionLimit属性
  • UseNagleAlgorithm属性

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