如何使用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;
}
}
}
我感谢这里那些尽力提供帮助的人。不幸的是,这需要致电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可能会有所帮助