Какой тайм-аут в 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 игнорирует эту конфигурацию и использует жестко закодированное значение для внутреннего использования? Попробовав что-то самостоятельно, я обнаружил, что добавлены новые команды netsh http для показа и добавления значений тайм-аута, поэтому, чтобы я пошел, я придумал следующую команду, но, к сожалению, это, похоже, не повлияло и на эту ошибку.
netsh http add timeouttypeout = значение IdleConnectionTimeout =300
5 ответов
Оказывается, эта проблема была вызвана значением тайм-аута соединения, используемым HTTP.sys, и это можно указать с помощью диспетчера IIS через дополнительные параметры для отдельного сайта. Это значение по умолчанию настроено на тайм-аут соединения, если заголовок и тело не были получены в течение 120 секунд. Если импульс данных от тела получен, то сервер перезапускает таймер (Timer_EntityBody) в пределах значения тайм-аута, после чего таймер сбрасывается для ожидания дополнительных данных.
Это так же, как указано в документации, касающейся Timer_EntityBody и connectionTimeout, однако это было трудно точно определить, поскольку кажется, что IIS Express игнорирует значение connectionTimeout, указанное в элементе limit в applicationhost.config, независимо от того, что говорится в документации. Чтобы определить это, мне пришлось установить полную версию IIS на мою машину для разработки и изменить вышеуказанные настройки после размещения там моего сайта.
Поскольку мы размещаем реальный сервис под IIS в Windows 2008, вышеупомянутое решение будет работать для меня, однако все еще остается вопрос о том, как правильно изменить значение времени ожидания подключения в тех случаях, когда вы являетесь хостингом.
Судя по ошибке:
Разъем подключения был прерван. Это может быть вызвано ошибкой обработки вашего сообщения или превышением тайм-аута приема удаленным хостом, или проблемой основного сетевого ресурса. Тайм-аут локального сокета был '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 -->
Параметр объясняется здесь
Вероятно, долго, но... Проверьте пул приложений IIS, если включен пинг. Это в расширенных настройках, группировка модели процесса.
Вы рассматривали http://support.microsoft.com/kb/946086?
Я наблюдал такие потоковые прерывания в моих расширениях ISAPI. После отключения буферизации в IIS 7 в соответствии с этим примечанием к поддержке все заработало.
Попробуйте это, для меня это решило проблему. Проблема в том, что ядро 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