Как заставить приложение C #, использующее HttpWebRequest, вести себя как скрипач - PullRequest
6 голосов
/ 30 июня 2011

У меня есть консольное приложение, которое использует около 20 потоков для подключения к удаленному веб-серверу и отправки произвольных http-запросов довольно небольшого размера, на 100% превышающих ssl. Удаленный веб-сервер на самом деле представляет собой весь центр обработки данных с балансировкой нагрузки, заполненный системами высокой доступности, которые могут обрабатывать сотни тысяч запросов в секунду. Это не проблема сервера или пропускной способности. С учетом сказанного я не запускаю его и не имею никакого влияния на его настройку, поэтому я не мог вносить изменения на стороне сервера, даже если бы захотел.

При запуске приложения с 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;
           }
        }
     }

Ответы [ 4 ]

5 голосов
/ 28 июля 2011

Я благодарю людей здесь, которые пытались помочь.К сожалению, для этого требовался звонок в службу поддержки Microsoft Profesional.

Несмотря на то, что я использовал ServicePointManager.Expect100Continue = false; Это происходило в конце жизненного цикла приложения.Просматривая журналы System.Net.Trace, мы увидели, что заголовок продолжения ожидаемого-100 все еще используется (кроме случаев использования fiddler).Решение состояло в том, чтобы поместить это в запуск приложения (в Main ())

Я также пытался прочитать поток ответа перед закрытием потока запроса.

После исправления все ускорилось.Приложение работает намного быстрее без Fiddler, чем с, что я и ожидал.

Несколько человек сказали, что вызовите dispose на HttpWebResponse.Этот класс не имеет публичного метода Dispose.Я предполагаю, что .Close () вызывает .Dispose () внутренне, хотя.

2 голосов
/ 30 июня 2011

Вы можете поэкспериментировать с «Вариантами подключения» Fiddler, чтобы увидеть, не является ли причиной высокой пропускной способности Fiddler повторное использование клиентских подключений.Если это так, вы можете рассмотреть возможность реализации общего безопасного пула http-соединений или просто посмотреть фильм или что-то в этом роде.^^

1 голос
/ 22 апреля 2013

Учитывая, что ваше приложение отправляет "произвольные http-запросы довольно небольшого размера", может помочь отключить алгоритм Nagle.

ServicePointManager.UseNagleAlgorithm = true;

С MSDN : ряд элементов может влиять на производительность при использовании HttpWebRequest, в том числе:

Алгоритм Нейгла [...] накапливает последовательности небольших сообщений в большие TCP-пакеты, прежде чем данные будут отправлены по сети. [...] Как правило, при постоянной пропускной способности большого объема улучшение производительности осуществляется с помощью алгоритма Nagle. Но для приложений с меньшей пропускной способностью может наблюдаться снижение производительности. [...] , если приложение использует соединения с малой задержкой, может помочь установить для этого свойства значение false.

1 голос
/ 30 июня 2011

Здесь можно сделать предположение, но это может быть связано с простой настройкой app.config:

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

У меня была одна и та же проблема в многопоточном приложении HTTP-запроса, и это решило эту проблему.

...