如何使使用 HttpWebRequest 的 C# 应用程序表现得像 fiddler
我有一个控制台应用程序,它使用 20 个左右的线程连接到远程 Web 服务器,并发送大小相当小的任意 http 请求,100% 通过 ssl。远程Web服务器实际上是一个完整的负载平衡数据中心,充满了高可用性系统,每秒可以处理数十万个请求。这不是服务器或带宽问题。话虽这么说,我不运行它,也对它的配置方式没有任何影响,所以即使我想,我也无法进行服务器端更改。
当使用 fiddler 运行应用程序时,应用程序的执行速度非常快。当不在 fiddler 中运行时,它真的要慢得多,以至于对手头的任务毫无用处。它似乎也在过程的早期某个时刻锁定了,但这可能只是一个死锁问题,我还不确定。
无论如何,fiddler 作为代理,无疑会以某种方式修改我的请求/连接,以确保出色的吞吐量,但我不知道它在做什么。我试图弄清楚这一点,以便我可以强制我的 .net 应用程序模仿 fiddler 连接处理行为,而无需实际通过 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;
}
}
}
I have a console app that uses 20 or so threads to connect to a remote web server and send arbitrary http requests rather small in size, 100% over ssl. The remote web server is actually an entire load balanced data center full of high availability systems that can handle hundreds of thousands of request per second. This is not a server or bandwidth issue. With that being said, I don't run it, nor do i have any influence in how it is configured, so I couldn't make server side changes even if I wanted to.
When running the app with fiddler the app performs amazingly fast. When not running in fiddler its really much slower, to the point of being useless for the task at hand. It also seems to lock up at some point rather early in the process, but this could simply be a deadlock issue, im not sure yet.
Anyhow, fiddler being a proxy , is undoubtedly modifying my requests/connections in some way that ensures wonderful throughput, however I have no idea what its doing. I am trying to figure it out so that I can force my .net application to mimic fiddlers connection handling behavior without actually having to run it through fiddler
I've pasted the connection code below.
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;
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我感谢这里试图提供帮助的人们。不幸的是,这需要致电微软专业支持。
即使我使用的是
ServicePointManager.Expect100Continue = false;
它发生在应用程序生命周期的后期。查看 System.Net.Trace 日志,我们发现 Expect-100 continue 标头仍在使用(使用 fiddler 时除外)。解决方案是将其放入应用程序启动中(在 Main() 中)我还尝试在关闭请求流之前读取响应流。
修复之后,一切都进展顺利。该应用程序在没有小提琴手的情况下运行得比有小提琴手的速度快得多,这正是我所期望的。
有几个人说要在 HttpWebResponse 上调用 dispose 。该类没有公共 Dispose 方法。我假设 .Close() 在内部调用 .Dispose() 。
I thank the folks here who tried to help. Unfortunately this needed a call to Microsoft Profesional Support.
Even though I was using
ServicePointManager.Expect100Continue = false;
It was happening to late in the app life cycle. Looking at the System.Net.Trace logs we saw that the expect-100 continue header was still being used (except when using fiddler). The solution was to put this into the app startup (in Main())I was also trying to read the response stream before closing the request stream.
After fixing that, everything sped up nicely. The app runs much faster without fiddler than with, which is what i would expect.
A couple people said to call dispose on on the HttpWebResponse. That class does not have a public Dispose method. I'm assuming .Close() calls .Dispose() internally though.
您可以使用 Fiddler 的“连接选项”来查看 Fiddler 强大吞吐量的原因是否是客户端连接的重用。如果是这种情况,您可能需要考虑实现共享安全 http 连接池或者只是去看电影什么的。 ^^
You can play around with Fiddler's "Connection Options" to see if the reason for Fiddler's powerfull throughput is reusing of client connections. If that's the case, you may want to consider implementing a shared secure http connection pool or just go watch a movie or something. ^^
在这里进行大胆的猜测,但这可能与简单的 app.config 设置有关:
我曾经在多线程 HTTP 请求应用程序中遇到过同样的问题,这解决了该问题。
Taking a wild guess here, but it might have to do with a simple app.config setting:
I had the same problem in a multi threaded HTTP requesting app once and this solved that problem.
鉴于您的应用程序发送“大小相当小的任意 http 请求”,禁用 Nagle 算法可能会有所帮助。
来自 MSDN:使用 HttpWebRequest 时,许多元素会影响性能,包括:
Given that your application sends "arbitrary http requests rather small in size", it may help to disable the Nagle algorithm.
From MSDN: A number of elements can impact performance when using HttpWebRequest, including: