2012-04-03 2 views
12

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

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

Обратите внимание, что это скорее вопрос 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, тогда как истинно с моей машины.

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

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

+1

Вы нашли решение? – Kugel

ответ

8

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

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

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

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

+0

Джесси, спасибо за информацию. Установка ServicePointManager DefaultConnectionLimit по-прежнему просто устанавливает его на ServicePoint/конечную точку, поэтому я не знаю, будет ли это обходить проблему блокировки, которую вы описываете на уровне SP. Я бы, конечно, ожидал, что ServicePoint будет узким местом, поскольку потоки/запросы будут расти, но не так резко. И все еще кажется, что запросы не выходят, но ответы ждут. Мне нужно будет изучить HttpClient в качестве альтернативы. – erasend

+0

Re: DefaultConnectionLimit - я не утверждаю, что он будет творить чудеса, но он обходит вызов блокировки (это) в установщике ConnectionLimit в самой службе. Что касается масштабирования, то мое впечатление было таким же. Я все еще не могу претендовать на то, чтобы по-настоящему понять, почему узкое место так сильно ударило. Есть некоторые конструкции синхронизации вокруг получения ответа и снова, если вы получаете потоки.В вашем случае я бы предположил, что переход с локального на нелокальный происходит из-за дополнительной задержки с внешним сервером ... но, опять же, это чистая спекуляция. –

+0

DefaultConnectionLimit ничего не менял (и удаление ConnectionLimit объекта SP вернул его по умолчанию 2). Дифференциал от локального к нелокальному определенно связан с Mono/environment, поскольку скрипт PHP не имел проблем - таких же запросов. Это то, что я пытаюсь понять на самом деле, исходя из предположения, что именно так Mono обрабатывает эти ответы. – erasend

8

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

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

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

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

+0

Может подтвердить, что 'ServicePointManager.DefaultConnectionLimit' не действует в Mono, ограничение по-прежнему 2 – KCD

+0

Установка его в [' app.config' или 'web.config'] (http://stackoverflow.com/a/436477/516748) работает ... однако доказывает обратную продуктивность в Mono с чем-либо, кроме 2, что значительно уменьшает пропускную способность (?!). Кроме того, ServicePoint.Connections всегда сообщает 0, тогда как Windows сообщает до и только через ConnectionLimit ... – KCD

0

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

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

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