Какой 130-секундный тайм-аут убивает мой вызов службы потоковой передачи WCF?

Совсем недавно я начал исследовать сложную проблему с потоковой передачей WCF, в которой создается исключение CommunicationException, если клиент ждет более 130 секунд между отправками на сервер.

Вот полное исключение:

System.ServiceModel.CommunicationException was unhandled by user code
  HResult=-2146233087
  Message=The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '23:59:59.9110000'.
  Source=mscorlib
  StackTrace:
    Server stack trace: 
       at System.ServiceModel.Channels.HttpOutput.WebRequestHttpOutput.WebRequestOutputStream.Write(Byte[] buffer, Int32 offset, Int32 count)
       at System.IO.BufferedStream.Write(Byte[] array, Int32 offset, Int32 count)
       at System.Xml.XmlStreamNodeWriter.FlushBuffer()
       at System.Xml.XmlStreamNodeWriter.GetBuffer(Int32 count, Int32& offset)
       at System.Xml.XmlUTF8NodeWriter.InternalWriteBase64Text(Byte[] buffer, Int32 offset, Int32 count)
       at System.Xml.XmlBaseWriter.WriteBase64(Byte[] buffer, Int32 offset, Int32 count)
       at System.Xml.XmlDictionaryWriter.WriteValue(IStreamProvider value)
       at System.ServiceModel.Dispatcher.StreamFormatter.Serialize(XmlDictionaryWriter writer, Object[] parameters, Object returnValue)
       at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Stream stream)
       at System.ServiceModel.Channels.HttpOutput.WriteStreamedMessage(TimeSpan timeout)
       at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
       at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
       at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
    Exception rethrown at [0]: 
       at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       at WcfService.IStreamingService.SendStream(MyStreamUpRequest request)
       at Client.Program.<Main>b__0() in c:\Users\jpierson\Documents\Visual Studio 2012\Projects\WcfStreamingTest\Client\Program.cs:line 44
       at System.Threading.Tasks.Task.Execute()
  InnerException: System.IO.IOException
       HResult=-2146232800
       Message=Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.
       Source=System
       StackTrace:
            at System.Net.Sockets.NetworkStream.MultipleWrite(BufferOffsetSize[] buffers)
            at System.Net.ConnectStream.InternalWrite(Boolean async, Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
            at System.Net.ConnectStream.Write(Byte[] buffer, Int32 offset, Int32 size)
            at System.ServiceModel.Channels.BytesReadPositionStream.Write(Byte[] buffer, Int32 offset, Int32 count)
            at System.ServiceModel.Channels.HttpOutput.WebRequestHttpOutput.WebRequestOutputStream.Write(Byte[] buffer, Int32 offset, Int32 count)
       InnerException: System.Net.Sockets.SocketException
            HResult=-2147467259
            Message=An existing connection was forcibly closed by the remote host
            Source=System
            ErrorCode=10054
            NativeErrorCode=10054
            StackTrace:
                 at System.Net.Sockets.Socket.MultipleSend(BufferOffsetSize[] buffers, SocketFlags socketFlags)
                 at System.Net.Sockets.NetworkStream.MultipleWrite(BufferOffsetSize[] buffers)
            InnerException: 

Похоже, что сервер преждевременно закрыл соединение из-за бездействия соединения. Если вместо этого я даю импульс серверу, хотя бы по одному байту за раз, я никогда не получу этого исключения и могу продолжать передавать данные бесконечно. Я создал очень простой пример приложения, чтобы продемонстрировать это, которое использует basicHttpBinding с Streamed transferMode, и я вставляю искусственную задержку из реализации настраиваемого потока на клиенте, которая задерживается на 130 секунд. Это имитирует что-то похожее на состояние недостаточной загрузки буфера, при котором поток, предоставленный в моем вызове службы от клиента, не передает данные в инфраструктуру WCF достаточно быстро, чтобы удовлетворить какое-то неидентифицируемое значение тайм-аута, которое, по-видимому, составляет около 130 секунд. отметка.

Используя инструменты трассировки службы WCF, я могу найти исключение HttpException с сообщением, которое гласит: «Клиент отключен, поскольку базовый запрос был выполнен. HttpContext больше не доступен».

В файле журнала трассировки IIS Express я вижу запись, в которой говорится: «Операция ввода-вывода была прервана из-за выхода потока или запроса приложения. (0x800703e3)»

Я настроил тайм-ауты сервера и клиента, чтобы использовать значение, превышающее 130-секундную отметку, просто чтобы исключить их. Я пробовал idleTimeout в IIS Express и множество значений тайм-аута, связанных с ASP.NET, чтобы выяснить, откуда эта проблема, но пока безуспешно. Лучшая информация, которую я могу найти на данный момент, - это комментарий в системе отслеживания проблем FireFox разработчика, который описывает аналогичную проблему, работающую вне архитектуры WCF. По этой причине я предполагаю, что проблема может быть больше связана с IIS7 или, возможно, с Windows Server.

Пользовательская привязка на сервере Web.config

<binding name="myHttpBindingConfiguration"
         closeTimeout="02:00:00"
         openTimeout="02:00:00"
         receiveTimeout="02:00:00"
         sendTimeout="02:00:00">
  <textMessageEncoding messageVersion="Soap11" />
  <httpTransport maxBufferSize="65536"                        
                 maxReceivedMessageSize="2147483647"
                 maxBufferPoolSize="2147483647"
                 transferMode="Streamed" />
</binding>

Конфигурация на стороне клиента в коде:

    var binding = new BasicHttpBinding();
    binding.MaxReceivedMessageSize = _maxReceivedMessageSize;
    binding.MaxBufferSize = 65536;
    binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
    binding.ReaderQuotas.MaxArrayLength = int.MaxValue;
    binding.TransferMode = TransferMode.Streamed;
    binding.ReceiveTimeout = TimeSpan.FromDays(1);
    binding.OpenTimeout = TimeSpan.FromDays(1);
    binding.SendTimeout = TimeSpan.FromDays(1);
    binding.CloseTimeout = TimeSpan.FromDays(1);

В ответ на идею wals попытаться увидеть, получаю ли я какие-либо другие результаты при самостоятельном размещении моей службы, я хочу добавить, что я сделал это и обнаружил, что получаю те же результаты, что и при размещении в IIS. Что это значит? Я предполагаю, что это означает, что проблема либо в WCF, либо в базовой сетевой инфраструктуре Windows. Я использую 64-разрядную версию Windows 7, и мы обнаружили эту проблему, запустив различные клиенты и запустив служебную часть на Windows 2008 Server.

Обновление 2013-01-15

Я нашел несколько новых подсказок благодаря DarkWanderer, когда понял, что WCF использует HTTP.sys в сценариях самостоятельного хостинга в Windows 7. Это заставило меня задуматься о том, что я могу настроить для HTTP.sys, а также о том, о каких проблемах сообщают люди. для HTTP.sys это похоже на то, что я испытываю. Это привело меня к файлу журнала, расположенному в C: \ Windows \ System32 \ LogFiles \ HTTPERR \ httperr1.log, который, по-видимому, регистрирует определенные типы проблем HTTP со стороны HTTP.sys. В этом журнале каждый раз, когда я запускаю свой тест, я вижу запись в журнале следующего типа.

2013-01-15 17:17:12 127.0.0.1 59111 127.0.0.1 52733 HTTP / 1.1 POST /StreamingService.svc - - Timer_EntityBody -

Итак, нужно выяснить, какие условия могут вызвать ошибку Timer_EntityBody и какие настройки в IIS7 или в другом месте могут иметь отношение к когда и если возникает эта ошибка.

На официальном веб-сайте IIS:

Соединение истекло до прибытия тела запроса. Когда становится ясно, что запрос имеет тело сущности, HTTP API включает таймер Timer_EntityBody. Первоначально предел этого таймера устанавливается равным значению connectionTimeout. Каждый раз, когда по этому запросу получено другое указание данных, HTTP API сбрасывает таймер, чтобы дать соединению больше минут, как указано в атрибуте connectionTimeout.

Попытка изменить атрибут connectionTimeout, как указано в приведенной выше ссылке, в applicationhost.config для IIS Express, похоже, не имеет никакого значения. Возможно, IIS Express игнорирует эту конфигурацию и внутренне использует жестко запрограммированное значение? Попробовав что-то самостоятельно, я обнаружил, что для отображения и добавления значений тайм-аута добавлены новые http-команды netsh, поэтому, давая мне понять, я придумал следующую команду, но, к сожалению, это не повлияло на эту ошибку.

netsh http add timeout timeouttype = значение IdleConnectionTimeout = 300


person jpierson    schedule 10.01.2013    source источник
comment
нам понадобится ваш файл app.config (с обеих сторон)   -  person wal    schedule 11.01.2013
comment
@wal - файлы конфигурации пока неинтересны. Я воспроизвожу это в простом тестовом приложении, но, чтобы удовлетворить просьбу, обновлю свой пост.   -  person jpierson    schedule 11.01.2013
comment
это время приема 2 минуты или 2 часа? :)   -  person wal    schedule 11.01.2013
comment
попробуйте изменить его на максимум: 24.20:31:23.6470000   -  person wal    schedule 11.01.2013
comment
2:00:00 - это 2 часа, и увеличение значения не имело значения.   -  person jpierson    schedule 11.01.2013
comment
Ok. Можете ли вы попробовать удалить назначенные вам значения для привязок на стороне клиента и сервера и посмотреть, получите ли вы какую-либо разницу? (может не решить проблему, но может сузить ее). Кроме того, можете ли вы запустить службу WCF изолированно (ее собственный процесс), чтобы исключить некоторые вещи?   -  person wal    schedule 11.01.2013
comment
@SebastianK - не более того, что я перечислил выше в своем посте.   -  person jpierson    schedule 14.01.2013
comment
Я устанавливаю их на клиенте и делаю это через код. См. Раздел «Конфигурация на стороне клиента в коде» в моем сообщении.   -  person jpierson    schedule 14.01.2013


Ответы (5)


Оказывается, эта проблема была вызвана значением Тайм-аут подключения. используется HTTP.sys, и это можно указать с помощью диспетчера IIS в расширенных параметрах для отдельного сайта. Это значение по умолчанию настроено на тайм-аут соединения, если и заголовок, и тело не были получены в течение 120 секунд. Если получен импульс данных от тела, то сервер перезапускает таймер (Timer_EntityBody) в пределах значения тайм-аута, затем таймер сбрасывается для ожидания дополнительных данных.

Настройка времени ожидания подключения в IIS

Это точно так же, как документация по Timer_EntityBody и connectionTimeout, однако это было трудно определить, поскольку оказалось, что IIS Express игнорирует значение connectionTimeout, указанное в элементе limits в applicationhost.config, независимо от того, что указано в документации. говорит. Чтобы определить это, мне пришлось установить полную версию IIS на моем компьютере разработки и изменить настройки выше после размещения там моего сайта.

Поскольку мы размещаем реальную службу под IIS в Windows 2008, вышеупомянутое решение будет работать для меня, однако все еще остается вопрос, как правильно изменить значение времени ожидания подключения в тех случаях, когда вы используете собственный хостинг.

person jpierson    schedule 15.01.2013
comment
Да, сначала я утомил 140, а затем протестировал с задержкой 133 и 160, чтобы убедиться, что он пройдет первый раз, а второй провалится, и он сделал именно это. Я провел дальнейшее тестирование на 400 и 600 секундах, чтобы подтвердить, что настройка действительно была значением, которое повлияло на это поведение. - person jpierson; 16.01.2013
comment
Кто-нибудь знает, намеревается ли MS исправить IIS Express, чтобы соблюсти значение connectionTimeout в пределах в applicationhost.config? - person petrsnd; 21.09.2013
comment
@jpierson Вы когда-нибудь догадывались, как увеличить время простоя для автономных служб? - person petrsnd; 25.09.2013
comment
В моем сценарии я не принимаю хостинг самостоятельно, поэтому мне не приходилось заниматься этим. Я был бы рад получить здесь любое представление по этому поводу, хотя на всякий случай, если кому-то из нас понадобится самостоятельно разместить и столкнуться с аналогичной проблемой. - person jpierson; 26.09.2013

Судя по ошибке:

Соединение с сокетом было прервано. Это может быть вызвано ошибкой при обработке вашего сообщения, превышением таймаута приема удаленным узлом или проблемой базового сетевого ресурса. Тайм-аут локального сокета был '23: 59: 59.9110000 '

Похоже, это простой тайм-аут TCP.

Вы можете проверить это, запустив приложение как локальное, а затем выполнив эту команду в консоли:

netstat -no |find "xxxxx"

где xxxxx - это PID вашего серверного процесса. Эта команда покажет вам соединения, установленные вашим сервером, и будет обновляться каждую секунду.

Попробуйте связаться с клиентом и посмотрите, что произойдет. Скорее всего, вы увидите «CLOSE_WAIT» или «TIME_WAIT» на своем соединении примерно через 100–120 секунд, что будет означать, что оно было прервано из-за тайм-аута.

Это можно исправить, добавив в конфигурацию следующее:

<httpTransport maxBufferSize="65536"                        
             maxReceivedMessageSize="2147483647"
             maxBufferPoolSize="2147483647"
             transferMode="Streamed"
             keepAliveEnabled="true" /> <!-- Here -->

Параметр объясняется здесь

person DarkWanderer    schedule 15.01.2013
comment
Я также предположил, что проблема должна быть связана с тайм-аутом более низкого уровня, например, в базовом сокете. Пока не уверен, будет ли значение тайм-аута на стороне сервера или на стороне клиента. Кроме того, значение keepAliveEnabled, похоже, не имело никакого эффекта. - person jpierson; 15.01.2013
comment
Все еще пытаюсь осмыслить вывод netstat, потому что он показывает неожиданный PID для процесса, который владеет открытым портом в моих тестах. Он показывает PID 4, который связан с System ntoskrnl.exe, а не с моим приложением командной строки для самостоятельного размещения. - person jpierson; 15.01.2013
comment
Что ж, что бы ни показало исследование PID, результат будет таким же ... Я думаю, keepAliveEnabled = true не влияет на transferMode = Streamed из-за природы потокового сокета. Если вы подтвердите причину, изложенную выше, я бы рекомендовал вам рассчитаться с собственными сообщениями поддержки активности, как вы описали в вопросе. - person DarkWanderer; 15.01.2013
comment
Оказывается, PID 4 - это ядро. Из того, что я могу понять, это обычный способ самостоятельного хостинга в Vista и выше, поскольку все размещается под HTTP.sys. Я попытался изменить значение idleConnectionTimeout для модуля HTTP.sys, запустив netsh http add timeout timeouttype = IdleConnectionTimeout value = 300, но, похоже, это не имело никакого значения. - person jpierson; 16.01.2013

Вероятно, долгая попытка, но ... Проверьте свой пул приложений IIS, включен ли в нем Ping. Это в расширенных настройках, группировка модели процесса.

person YuryP    schedule 10.01.2013
comment
Я изучил это и, к сожалению, воспроизводю это прямо сейчас в IIS Express, и установка значения pingFrequency в applicationhost.config только вызвала сбой IIS Express при запуске. Мне кажется, что, возможно, IIS Express не имеет той же модели процесса или конфигурации модели процесса, что и полный IIS? Возможно, это также указывает на то, что это не источник проблемы, поскольку я могу воспроизвести это точно так же, независимо от того, использую ли я службу в IIS7 или IIS Express. - person jpierson; 11.01.2013
comment
Документация по элементу ‹processModel› должна быть устаревшей. Я нашел и попытался использовать атрибуты pingingEnabled, pingInterval и pingResponseTime, но, к сожалению, я не наткнулся на комбинацию, которая работает. - person jpierson; 11.01.2013
comment
да, тогда, наверное, не связаны. Я думал, что установка piningEnabled на false или увеличение pingResponseTime поможет. - person YuryP; 11.01.2013
comment
Обдумывать те же самые идеи - это первое, что я пробовал после того, как вы представили мне эти настройки. Спасибо за ваш вклад. - person jpierson; 11.01.2013

Попробуйте это, для меня это решило проблему. Проблема в том, что базовое ядро ​​http.sys имеет собственный тайм-аут и разрывает соединение.

http://mmmreddy.wordpress.com/2013/07/11/wcf-use-of-http-transport-sharing-persistent-tcp-sessions/

netsh http add timeout timeouttype=idleconnectiontimeout value=120
person Luiz Felipe    schedule 27.08.2013
comment
См. В конце моего вопроса, я действительно пробовал использовать этот параметр в моем случае. - person jpierson; 28.08.2013

Вы рассматривали http://support.microsoft.com/kb/946086?

Я наблюдал такие прерывания потоковой передачи в своих расширениях ISAPI. После отключения буферизации в IIS 7 в соответствии с этим примечанием о поддержке все работало оштрафовано.

person must21    schedule 10.10.2013
comment
Нет, я не встречал эту статью в базе знаний, но мне она кажется несколько несвязанной. Я очень конкретно столкнулся с проблемой тайм-аута, а не с проблемой, которая связана с тем, что буфер достигает пределов своей емкости. Я уверен в этом, потому что принятый ответ, который влечет за собой изменение тайм-аута в IIS, напрямую решил эту проблему. - person jpierson; 14.10.2013