Параллельные запросы WCF накапливаются на сервере при использовании WSHttpBinding

У меня есть приложение клиент / сервер WCF, которое общается по HTTP с помощью WSHttpBinding.

Настройка сервера: сам хостинг, с использованием стандартного WCF ServiceHost, Мой фактический класс обслуживания приписывается как:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, 
 InstanceContextMode = InstanceContextMode.PerSession, 
 UseSynchronizationContext = false)]

Настройка клиента: использование клиентского прокси-сервера, сгенерированного Visual-Studio, с использованием синхронных вызовов службы (proxy.call_server_method блокирует до тех пор, пока сервер не ответит полностью.)

Сценарий: у меня есть один конкретный вызов метода, выполнение которого на сервере занимает 20 секунд. Клиент вызывает этот метод в отдельном потоке, поэтому он не задерживается, и ConcurrencyMode.Multiple означает, что WCF должен выполнять его также в отдельном потоке на сервере.

Эта теория подтверждается тем фактом, что при настройке приложения NetTcpBindingвсе работает нормально.

Проблема:
Если я настрою приложение для использования WSHttpBinding, затем этот длинный вызов метода заставляет http-запросы "резервировать". Я проверил это поведение как путем проверки моих журналов, так и путем отладки HTTP-запросов с использованием fiddler.

Пример:

  • Клиент инициирует 20-секундный длинный запрос в фоновом потоке
  • Клиент инициирует запрос B и C в основном потоке
  • Запросы B и C отправляются на сервер, который не обрабатывает их, пока не выполнит 20-секундный длинный запрос.

Но иногда:

  • Запросы B и C не отправляются (они даже не появляются в fiddler), пока не вернется 20-секундный запрос (это редко).
    • Примечание: настройка <add address="*" maxconnection="100"/> в app.config клиента это перестало происходить.
  • Запрос B отправляется и получает ответ немедленно, в то время как запрос C задерживается до завершения 20-секундного (это редко)

Вот график от Fiddler, демонстрирующий проблему: (нажмите для увеличения)

http://farm4.static.flickr.com/3311/3510636841_2a27435eec_o.png

Как вы можете видеть, все запросы сохраняются на сервере. Когда 20-секундный запрос завершается, все ответы поступают, но обратите внимание, что есть некоторые запросы, которые не задерживаются...

Итак, вопросы:

  • Какого черта здесь происходит? Почему это работает нормально, используя NetTcpBinding и не работать с помощью WSHttpBinding?
  • Почему противоречивое поведение?
  • Что я могу сделать, чтобы это исправить?

Заметки:

  • Это не блокировка на сервере. Я установил точки останова и использовал !syncblk и это последовательно сообщает, что никакие замки не удерживаются.
  • Это не моя нить (иначе NetTcpBinding не должен работать)
  • я имею <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000" /> установить в app.config сервера
  • 20-секундный вызов просто ждет по таймеру, он не перебивает процессор, диск или сеть
  • Я бы предпочел решение, которое не включало бы повторную архитектуру приложения для использования асинхронных вызовов... это большая куча устаревшего кода, и я действительно не хочу возиться с вещами, которые я не понимаю.

8 ответов

Решение

[Само-ответ, чтобы показать другим пользователям, каким было наше возможное решение]

В конце концов мне так и не удалось решить это.
Нашим окончательным решением было переключить наше приложение с WSHttpBinding и на NetTcpBinding в производстве - мы планировали сделать это в конце концов по соображениям производительности.

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

За пределами WCF есть некоторый дроссель (.Net или Windows), который по умолчанию разрешает использовать не более двух одновременных исходящих HTTP-соединений. К сожалению, я не могу вспомнить на всю жизнь название этой вещи (и что бы вы положили в app.config или ваше приложение, чтобы переопределить это). Учитывая, что вы не видите, что запросы покидают клиент, и что это только HTTP, я думаю, что вы нажимаете "эту вещь". Я буду продолжать искать его имя.

Обновление: нашел - попробуйте это на клиенте (но измените '2' на большее число):

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

Мы видели точно такие же симптомы со службой JSON, размещенной в IIS/ASP.NET.

Основная причина в том, что ASP.NET синхронизирует запросы, а не WCF. Нам пришлось отключить состояние сеанса (на уровне приложения), чтобы получить параллельные методы WCF.

Web.config:<system.web> <sessionState mode="Off" /> </system.web>

Обратите внимание, что наш сервис использует webHttpBinding, а не wsHttpBinding. Так что я не уверен, что это также решит проблему Ориона.

Я думаю, что вы достигли лимита протокола, и чтобы обойти его, вам нужно изменить стандартные настройки на клиентском компьютере:

http://support.microsoft.com/kb/183110

http://support.microsoft.com/kb/282402

Я думаю, WSHttpBinding использует настройки WinINET при выдаче запросов.

Рассмотрите возможность использования ConcurrencyMode.Multiple в службах для каждого вызова, чтобы разрешить одновременные вызовы.

Если вы измените на BasicHttpBinding, это работает?

Это так, похоже, это твоя проблема, душит сессия, что-то, что укусило меня в задницу.

Я забыл - это может быть заказ? Я думаю, что, возможно, RM поверх http сохраняет порядок, но, возможно, сеансы Tcp нет (если вы явно не запрашиваете это)? Есть ли в договоре на обслуживание атрибут, описывающий заказанные / неупорядоченные сеансы (я забыл).

Не уверен, но иногда проблема с одновременными вызовами из приложения silverlight связана с управлением подключением браузера. Для меня решение было поместить это в наш App.xaml.cs, метод Application_Startup, как описано здесь: http://weblogs.asp.net/olakarlsson/simultaneously-calling-multiple-methods-on-a-wcf-service-from-silverlight

WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
Другие вопросы по тегам