簡體   English   中英

如何使用 HttpWebRequest 使 C# 應用程序表現得像提琴手

[英]How to make a C# application using HttpWebRequest behave like fiddler

我有一個控制台應用程序,它使用 20 個左右的線程連接到遠程 web 服務器並發送任意 http 請求,其大小相當小,比 ZF9D5C16A7F42203F8C195432354A23 高 100%。 遠程 web 服務器實際上是一個完整的負載平衡數據中心,其中包含每秒可以處理數十萬個請求的高可用性系統。 這不是服務器或帶寬問題。 話雖如此,我沒有運行它,也沒有影響它的配置方式,所以即使我想也無法進行服務器端更改。

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

我感謝這里試圖提供幫助的人們。 不幸的是,這需要致電 Microsoft 專業支持。

即使我使用的是ServicePointManager.Expect100Continue = false; 它發生在應用程序生命周期的后期。 查看 System.Net.Trace 日志,我們看到 expect-100 continue header 仍在使用中(使用提琴手時除外)。 解決方案是將其放入應用程序啟動中(在 Main() 中)

在關閉請求 stream 之前,我還試圖閱讀響應 stream。

修復后,一切都很好地加速了。 該應用程序在沒有 fiddler 的情況下運行得比使用 fiddler 快得多,這是我所期望的。

有幾個人說要在 HttpWebResponse 上調用 dispose。 class 沒有公共的 Dispose 方法。 我假設.Close() 在內部調用.Dispose()。

您可以使用 Fiddler 的“連接選項”來查看 Fiddler 強大吞吐量的原因是否是重用客戶端連接。 如果是這種情況,您可能需要考慮實現共享安全 http 連接池,或者只是 go 看電影或其他東西。 ^^

鑒於您的應用程序發送“任意大小的 http 請求”,它可能有助於禁用 Nagle 算法。

ServicePointManager.UseNagleAlgorithm = true;

來自MSDN :使用 HttpWebRequest 時,許多元素會影響性能,包括:

Nagle 算法 [...] 在通過網絡發送數據之前,將小消息序列累積到更大的 TCP 數據包中。 [...] 通常,對於恆定的大容量吞吐量,使用 Nagle 算法可以提高性能。 但是對於較小吞吐量的應用程序,可能會看到性能下降。 [...]如果應用程序正在使用低延遲連接,則將此屬性設置為 false 可能會有所幫助。

在這里進行瘋狂的猜測,但這可能與簡單的 app.config 設置有關:

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

我在請求應用程序一次的多線程 HTTP 中遇到了同樣的問題,這解決了這個問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM