HttpWebResponse не масштабируется для одновременных исходящих запросов - PullRequest
12 голосов
/ 03 апреля 2012

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

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

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

Вот некоторый псевдокод:

// 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 и получить то же среднее время отклика с минимальными изменениями, будь то 1 пользователь или 100.

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

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

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

Я прошел через все настройки конфигурации, которые я могу придумать для Mono и Apache, и ЕДИНСТВЕННЫЙ параметр, который отличается в двух средах (по крайней мере, в коде), заключается в том, что иногда ServicePoint SupportsPipelining = false в Mono, хотя правда от моей машины.

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

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

Ответы [ 3 ]

8 голосов
/ 11 сентября 2012

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

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

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

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

8 голосов
/ 03 апреля 2012

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

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

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

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

0 голосов
/ 01 августа 2013

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

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

  1. Попробуйте использовать сборщик мусора SGen вместо старого boehm, передав --gc=sgen в качестве флага для моно.
  2. Если вышеперечисленное не помогает, обновитесь до Mono 3.2 (кстати, по умолчанию это также SGEN GC), потому что с тех пор, как вы задали вопрос, было много исправлений.
  3. Если приведенное выше не помогает, создайте свой собственный Mono (основная ветвь), так как этот важный запрос о многопоточности недавно был объединен.
  4. Если вышеописанное не помогает, создайте свой собственный Mono с добавленным этим запросом на извлечение . Если это решит вашу проблему, пожалуйста, добавьте «+1» к запросу. Это может быть исправление для ошибка 7055 .
...