Подтвердить что ты не робот

HttpWebResponse не будет масштабироваться для одновременных исходящих запросов

У меня есть серверное приложение ASP.NET 3.5, написанное на С#. Он отправляет исходящие запросы в REST API с помощью HttpWebRequest и HttpWebResponse.

Я установил тестовое приложение для отправки этих запросов на отдельные потоки (чтобы смутно имитировать concurrency для сервера).

Обратите внимание, что это скорее вопрос Mono/Environment, чем вопрос с кодом; поэтому имейте в виду, что приведенный ниже код не является дословным; просто вырезать/вставить функциональные биты.

Вот несколько псевдокодов:

// threaded client piece
int numThreads = 1;
ManualResetEvent doneEvent;

using (doneEvent = new ManualResetEvent(false))
        {

            for (int i = 0; i < numThreads; i++)
            {

                ThreadPool.QueueUserWorkItem(new WaitCallback(Test), random_url_to_same_host);

            }
            doneEvent.WaitOne();
        }

void Test(object some_url)
{
    // setup service point here just to show what config settings Im using
    ServicePoint lgsp = ServicePointManager.FindServicePoint(new Uri(some_url.ToString()));

        // set these to optimal for MONO and .NET
        lgsp.Expect100Continue = false;
        lgsp.ConnectionLimit = 100;
        lgsp.UseNagleAlgorithm = true;
        lgsp.MaxIdleTime = 100000;        

    _request = (HttpWebRequest)WebRequest.Create(some_url);


    using (HttpWebResponse _response = (HttpWebResponse)_request.GetResponse())
    {
      // do stuff
    } // releases the response object

    // close out threading stuff

    if (Interlocked.Decrement(ref numThreads) == 0)
    {
        doneEvent.Set();
    }
}

Если я запустил приложение на своей локальной машине разработки (Windows 7) на веб-сервере Visual Studio, я могу запустить numThreads и получить такое же время отклика avg с минимальным изменением, будь то 1 "пользователь" или 100.

Публикация и развертывание приложения в Apache2 в среде Mono 2.10.2, время отклика масштабируется почти линейно. (т.е. 1 нить = 300 мс, 5 ниток = 1500 мс, 10 потоков = 3000 мс). Это происходит независимо от конечной точки сервера (другое имя хоста, другая сеть и т.д.).

Используя IPTRAF (и другие сетевые инструменты), похоже, что приложение открывает только 1 или 2 порта для маршрутизации всех подключений и оставшихся ответов ждать.

Мы создали аналогичное PHP-приложение и развернуто в Mono с одинаковыми запросами и шкалой ответов соответственно.

Я пропустил каждый параметр конфигурации, о котором я могу думать о Mono и Apache, и параметр ONLY, который отличается от двух сред (по крайней мере, в коде), заключается в том, что иногда ServicePoint SupportsPipelining = false в Mono, в то время как это true от моей машины.

Кажется, что ConnectionLimit (по умолчанию 2) по какой-то причине не изменяется в Mono, но я устанавливаю его более высокое значение как в коде, так и в файле web.config для указанных узлов.

Либо я, и моя команда упускают из виду что-то значительное или это какая-то ошибка в Mono.

4b9b3361

Ответ 1

Я считаю, что вы столкнулись с узким местом в HttpWebRequest. В веб-запросах каждый использует общую инфраструктуру точек обслуживания в рамках платформы .NET. Это, как представляется, предназначено для повторного использования запросов на один и тот же хост, но по моему опыту возникают два узких места.

Во-первых, точки обслуживания допускают только два одновременных подключения к данному хосту по умолчанию, чтобы соответствовать спецификации HTTP. Это можно переопределить, установив статическое свойство ServicePointManager.DefaultConnectionLimit на большее значение. Подробнее см. MSDN. Похоже, вы уже обращаетесь к этому для самой отдельной точки обслуживания, но из-за схемы блокировки concurrency на уровне точки обслуживания это может способствовать узкому месту.

Во-вторых, возникает проблема с детализацией блокировки в классе ServicePoint. Если вы декомпилируете и посмотрите на источник ключевого слова lock, вы обнаружите, что он использует сам экземпляр для синхронизации и делает это во многих местах. Когда экземпляр точки обслуживания разделяется между веб-запросами для данного хоста, по моему опыту это имеет тенденцию к узкому месту, так как больше HttpWebRequests открывается и заставляет его плохо масштабироваться. Этот второй пункт - это, в основном, личное наблюдение и тряска вокруг источника, поэтому возьмите его с солью; Я бы не считал это авторитетным источником.

К сожалению, я не нашел разумной замены в то время, когда я работал с ней. Теперь, когда веб-API ASP.NET был выпущен, вы можете обратиться к HttpClient. Надеюсь, что это поможет.

Ответ 2

Я знаю, что это довольно старый, но я делаю это здесь, если это может помочь кому-то другому, кто сталкивается с этой проблемой. Мы столкнулись с той же проблемой с параллельными исходящими HTTPS-запросами. В игре есть несколько проблем.

Первая проблема заключается в том, что ServicePointManager.DefaultConnectionLimit не изменил лимит подключения, насколько я могу судить. Установив это значение 50, создав новое соединение, а затем проверим ограничение соединения на точке обслуживания для нового подключения, выполните следующие действия. 2. Установив его на этой точке обслуживания до 50, как представляется, работает и сохраняется для всех подключений, которые в конечном итоге пройдут эта точка обслуживания.

Вторая проблема, с которой мы столкнулись, - это потоки. Текущая реализация пула моно потоков, по-видимому, создает не более 2 новых потоков в секунду. Это вечность, если вы делаете много параллельных запросов, которые начинаются точно в одно и то же время. Чтобы противодействовать этому, мы попытались установить ThreadPool.SetMinThreads на большее число. Похоже, что Mono создает только один новый поток при выполнении этого вызова, независимо от дельта между текущим числом потоков и желаемым номером. Мы смогли обойти это, вызвав SetMinThreads в цикле, пока пул потоков не будет иметь желаемое количество простоя.

Я открыл ошибку в последнем выпуске, потому что тот, который я наиболее уверен, работает не так, как предполагалось: https://bugzilla.xamarin.com/show_bug.cgi?id=7055

Ответ 3

Если @jake-moshenko прав насчет ServicePointManager.DefaultConnectionLimit не имеет никакого эффекта, если он изменен в Mono, пожалуйста, укажите это как ошибку в http://bugzilla.xamarin.com/.

Однако я бы попробовал некоторые вещи, прежде чем полностью отбросить это как проблему Mono:

  • Попробуйте использовать сборщик мусора SGen вместо старого behm one, передав --gc=sgen в качестве флага для моно.
  • Если вышеизложенное не помогает, перейдите на Mono 3.2 (кстати, BTW по умолчанию использует SGEN GC), потому что было много исправлений, так как вы задали вопрос.
  • Если приведенное выше не помогает, создайте свою собственную Mono (ведущую ветвь), так как этот важный запрос на перенос о потоковом соединении недавно.
  • Если приведенное выше не помогает, добавьте свой собственный моно с этот запрос на перенос. Если он исправляет вашу проблему, добавьте "+1" к запросу pull. Это может быть исправление для ошибка 7055.