Нет потоков для обработки Socket.AsyncReceive из-за одновременных запросов к службе WCF.

У меня есть служба WCF, размещенная в IIS, которая по запросу выполняет запрос на другой сервер с использованием асинхронных запросов сокетов. Запрос должен быть быстрым (5 секунд), иначе время ожидания истечет. Когда много пользователей отправляют запросы одновременно (я думаю, около 10), то происходит то, что запросы сокетов к другому серверу не обрабатываются в течение этих 5 секунд. Журналы на другом сервере говорят, что запрос получен и обработан почти сразу, а ответ отправлен обратно, но асинхронное событие не запускается до 5-10 секунд, что приводит к тайм-ауту службы wcf (обратите внимание, что это не тайм-аут из сам wcf, но внутренний тайм-аут при вызове на другой сервер). Мы думаем, что этот пул либо слишком медленный, чтобы порождать новые потоки, либо имеет какие-то причины этого не делать, что приводит к ситуации, когда нет ни одного потока, доступного для получения асинхронного получения сокета. Насколько я понимаю, обработчики сокетов WCF и async используют для этого ThreadPool, поэтому они действительно борются за один и тот же ресурс. Кто-то сталкивался с подобной ситуацией? Каков наилучший способ справиться с этим?

Я вижу два простых выхода:

1) Ограничьте количество максимальных одновременных запросов в службе wcf до 5 или что-то еще, что может замедлить ее работу, поскольку некоторым запросам придется ждать в очереди.

2) Увеличить количество MinSize в ThreadPool Буду рад любым предложениям по этому поводу.

P.S. Процессор имеет 8 ядер, что означает, что минимальный размер пула потоков по умолчанию равен 8, поэтому я начинаю видеть проблему, когда происходит более 8 одновременных запросов.


person Ilya Chernomordik    schedule 06.12.2013    source источник
comment
Проблема почти наверняка связана с работой, которую вы выполняете в ThreadPool. Вы блокируете ввод-вывод при обращении к другому серверу? Если это так, вы будете бессмысленно нагружать ThreadPool и подвергаться задержке, связанной с решением ThreadPool о запуске новых потоков.   -  person spender    schedule 06.12.2013
comment
Я не блокирую сам ввод-вывод, поскольку это асинхронный сокет, но поток wcf заблокирован и ожидает результатов, поскольку в wcf нет возможности получить асинхронный ответ.   -  person Ilya Chernomordik    schedule 06.12.2013
comment
нет возможности получить асинхронный ответ? Вы в этом уверены? msdn.microsoft.com/en-us/library/ms731177.aspx   -  person spender    schedule 06.12.2013
comment
Ваша ссылка касается асинхронного вызова службы wcf, я имею в виду, что я не могу отвечать асинхронно (например, в MVC), я должен отвечать в методе, вызываемом wcf синхронно из службы WCF. P.S. Я сам не делаю вызовов wcf   -  person Ilya Chernomordik    schedule 06.12.2013
comment
Я вызываю вас на это. Вы можете написать контракты, которые возвращают Task<T>, и использовать async/await в своем сервисном методе. Если вам нужна масштабируемость, это единственный путь. blogs.msdn.com/b/endpoint/archive/2010/11/13/   -  person spender    schedule 06.12.2013
comment
Я понимаю, что вы имеете в виду, но я не могу изменить контракт, к сожалению... Но спасибо за ссылку   -  person Ilya Chernomordik    schedule 06.12.2013
comment
Как вы обрабатываете асинхронные ответы? Меня интересуют журналы на другом сервере, в которых говорится, что запросы обрабатываются и на них сразу же дается ответ, но время ожидания первого сервера все еще истекает.   -  person HasaniH    schedule 06.12.2013
comment
Я использую m_Socket.ReceiveAsync(e)   -  person Ilya Chernomordik    schedule 06.12.2013
comment
Возможно, лучше выполнять ввод-вывод синхронно, чтобы избежать использования застрявшего ThreadPool. Поскольку вы блокируете ThreadPool, ожидая ввода-вывода, почему бы просто не выполнить его синхронно? Если вы потратили ThreadPool на 8 запросов, то не будет потоков ThreadPool, доступных для обработки обратного вызова полученных данных из асинхронного вызова сокета. Это что-то вроде медленного выхода из тупика. Я по-прежнему убежден, что лучше всего переписать контракт, чтобы разрешить async/await, и за что вы должны бороться. Существующие клиенты не будут знать об изменении.   -  person spender    schedule 06.12.2013
comment
Возможно ли, что при обработке полученных данных вы пропустите ответ, скажем, если несколько ответов будут возвращены в один и тот же буфер? Если вы проверите проводной трафик, ответы отсутствуют или просто задерживаются?   -  person HasaniH    schedule 06.12.2013
comment
@SpaceghostAli Я не думаю, что это проблема, код может обрабатывать несколько, один или частичный ответ в буфере.   -  person Ilya Chernomordik    schedule 09.12.2013
comment
@spender Я на .NET 4, он там доступен? Насколько я понимаю, это новое в .NET 4.5?   -  person Ilya Chernomordik    schedule 09.12.2013


Ответы (1)


Если вы блокируете ожидание асинхронного ввода-вывода, вы создаете ситуацию, которая гарантированно приведет к голоданию потока ThreadPool.

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

Вам нужно переписать свои сервисные контракты, чтобы они возвращали Task<T> вместо обычного T. Ваши клиенты даже не заметят, но вы сможете без малейшего напряжения запускать тысячи вместо 8 одновременных клиентов (при условии, что фактическая рабочая нагрузка тривиальна), приняв async/await.

Если это невозможно, рассмотрите возможность выполнения ввода-вывода с использованием синхронных методов, поскольку в настоящее время поток, в котором вы работаете, и поток, от которого вы зависите, чтобы продолжить, выполняются в одном и том же ограниченном ThreadPool. С синхронным вводом-выводом вы устраните большую часть проблемы, хотя она по-прежнему (IMO) будет серьезно затруднена из-за того, что вы не напишете асинхронный сервисный метод.

РЕДАКТИРОВАТЬ

Поскольку вы используете более старую версию .Net, вы, вероятно, не можете использовать async/await. Существует несколько других вариантов написания асинхронных служб, задокументированных здесь. IIRC, хотя вы меняете контракт для реализации этих методов, конечные точки w.r.t. клиент остается неизменным, пока вы соблюдаете соглашения, изложенные в документации.

person spender    schedule 06.12.2013
comment
Большое спасибо за ответ, к сожалению, синхронный ввод-вывод не вариант, так как у меня один сокет, и если я буду выполнять ввод-вывод синхронно, это будет просто очень медленно, так как мне придется делать какую-то блокировку. Что касается другого предложения вернуть Task‹T›, почему вы говорите, что клиент не заметит разницы? Это будет изменение контракта, не так ли? Я на .NET 4, он там доступен? Насколько я понимаю, это новое в .NET 4.5? - person Ilya Chernomordik; 09.12.2013