Одновременные запросы WCF накапливаются на сервере при использовании WSHttpBinding
-
08-07-2019 - |
Вопрос
У меня есть клиент/серверное приложение WCF, которое обменивается данными через HTTP с помощью WSHttpBinding.
Настройка сервера:самостоятельный хостинг с использованием стандарта WCF ServiceHost
.Мой фактический класс обслуживания определяется как:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.PerSession,
UseSynchronizationContext = false)]
Настройка клиента:используя клиентский прокси, созданный визуальной студией, используя синхронный сервисные звонки(proxy.call_server_method
блокируется до тех пор, пока сервер не ответит полностью.)
Сценарий:У меня есть один конкретный вызов метода, выполнение которого на сервере занимает 20 секунд.Клиент вызывает этот метод в отдельном потоке, поэтому он не задерживается, и ConcurrencyMode.Multiple
означает, что WCF также должен выполнить его в отдельном потоке на сервере.
Эта теория подтверждается тем фактом, что когда я настраиваю свое приложение на использование NetTcpBinding
, все работает нормально.
Проблема:
Если я настрою приложение для использования WSHttpBinding
, то этот длинный вызов метода приводит к «резервному копированию» http-запросов.Я проверил это поведение как путем проверки своих журналов, так и путем отладки HTTP-запросов с помощью Fiddler.
Пример:
- Клиент инициирует 20-секундный запрос в фоновом потоке
- Клиент инициирует запросы B и C в приоритетном потоке
- Запросы B и C отправляются на сервер, который не обрабатывает их, пока не будет выполнен 20-секундный запрос.
Но иногда:
- Запросы B и C не получить отправку (они даже не появляются в скрипаче) до тех пор, пока не вернется 20-секундный запрос (это бывает редко).
- Примечание:параметр
<add address="*" maxconnection="100"/>
в клиентском app.config это (по-видимому) прекратилось.
- Примечание:параметр
- Запрос B отправляется и получает ответ немедленно, а запрос C задерживается до завершения 20-секундного запроса (это бывает редко).
Вот временная шкала скрипача, демонстрирующая проблему:(нажмите, чтобы увеличить версию)
Как видите, все запросы резервируются на сервере.После завершения 20-секундного запроса все ответы поступают потоком, но обратите внимание, что есть некоторые запросы, которые нет задерживают...
Так, Вопросы:
- Какого черта здесь происходит?Почему это работает нормально, используя
NetTcpBinding
и не работать с использованиемWSHttpBinding
? - Почему такое непоследовательное поведение?
- Что я могу сделать, чтобы это исправить?
Примечания:
- Сервер не блокируется.Я установил точки останова и использовал
!syncblk
и он постоянно сообщает, что блокировки не удерживаются. - Это не моя нить (в противном случае NetTcpBinding не должен работать)
- У меня есть
<serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000" />
установить в app.config сервера - 20-секундный вызов просто ожидает таймера, он не нагружает процессор, диск или сеть.
- Я бы предпочел решение, которое не требовало бы перепроектирования приложения для использования асинхронных вызовов...это большая куча устаревшего кода, и я действительно не хочу возиться с вещами, которых не понимаю.
Решение 3
[Ответ для себя, чтобы показать другим пользователям, каким было наше окончательное решение]
В конце концов мне так и не удалось это решить.
Нашим окончательным решением было отключить наше приложение от 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.
Веб.конфигурация:<system.web> <sessionState mode="Off" /> </system.web>
Обратите внимание, что наш сервис использует webHttpBinding, а не wsHttpBinding.Так что я не уверен, что это решит и проблему Ориона.
Я думаю, вы достигли предела протокола, и чтобы обойти его, вам нужно изменить стандартные настройки на клиентском компьютере:
http://support.microsoft.com/kb/183110
http://support.microsoft.com/kb/282402
Я предполагаю, что WSHttpBinding использует настройки WinINET при выдаче запросов.
Если вы перейдете на BasicHttpBinding, это сработает?
Так ли это звучит это твоя проблема, Регулирование сеанса, что-то, что укусило меня за задницу.
Рассмотрите возможность использования ConcurrencyMode.Multiple на услуги по вызову, чтобы разрешить одновременное звонки.
Я забыл - может это был заказ?Я думаю, возможно, RM через http сохраняет порядок, а может быть, сеансы Tcp - нет (если вы явно не запросите этого)?Есть ли в контракте на обслуживание атрибут, описывающий упорядоченные/неупорядоченные сеансы (я забыл).
Не уверен, но иногда проблемы с одновременными вызовами из приложения Silverlight связаны с управлением соединениями браузера.Для меня решением было поместить это в наш метод App.xaml.cs, Application_Startup, как описано здесь: http://weblogs.asp.net/olakarlsson/simulentially-calling-multiple-methods-on-a-wcf-service-from-silverlight
WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);