HTTP.SYS desconecta por la fuerza los enlaces HTTP para los servicios auto hospedados de WCF
Este artículo le ayuda a resolver el problema que hace que Windows Server 2012 servidor basado en R2 coloque una conexión TCP subyacente.
Versión del producto original: Windows Communication Foundation
Número KB original: 3137046
Síntomas
En un servicio Windows Communication Foundation (WCF) auto hospedado en un servidor que ejecuta Windows Server 2012 R2 y que usa enlaces basados en HTTP (por ejemplo), el servidor quita intermitentemente la conexión basicHttpBinding TCP subyacente. Cuando se produce este problema, puede verlo en los registrosHTTP.SYS que se encuentran normalmente en la C:\WINDOWS\System32\LogFiles\HTTPERR carpeta. Los archivos de registro deben tener una entrada que cite Timer_MinBytesPerSecond como motivo. Este problema no se produce en Windows Server 2008 R2.
La entrada de registro es similar a la siguiente:
#Fields: date time c-ip c-port s-ip s-port cs-version cs-method cs-uri sc-status s-siteid s-reason s-queuename
date time 10.145.136.58 41079 10.171.70.136 8888 HTTP/1.1 POST /MySelfHostedService/TestService1 - - Timer_MinBytesPerSecond -
date time 10.145.136.58 41106 10.171.70.136 8888 HTTP/1.1 POST /MySelfHostedService/TestService1 - - Timer_MinBytesPerSecond -
date time 10.145.136.58 40995 10.171.70.136 8888 HTTP/1.1 POST /MySelfHostedService/TestService1 - - Timer_MinBytesPerSecond -
date time 10.145.136.58 41022 10.171.70.136 8888 HTTP/1.1 POST /MySelfHostedService/TestService1 - - Timer_MinBytesPerSecond -
En los seguimientos wcf, el servicio produce un error durante una operación de bytes de recepción con System.Net.HttpListenerException , como en el ejemplo siguiente:
<Exception>
<ExceptionType>System.ServiceModel.CommunicationException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>The I/O operation has been aborted because of either a thread exit or an application request</Message>
<StackTrace>
at System.ServiceModel.Channels.HttpRequestContext.ListenerHttpContext.ListenerContextHttpInput.ListenerContextInputStream.EndRead(IAsyncResult result)
at System.ServiceModel.Channels.HttpInput.ParseMessageAsyncResult.OnRead(IAsyncResult result)
at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.HttpRequestStream.HttpRequestStreamAsyncResult.IOCompleted(HttpRequestStreamAsyncResult asyncResult, UInt32 errorCode, UInt32 numBytes)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
<ExceptionString>System.ServiceModel.CommunicationException: The I/O operation has been aborted because of either a thread exit or an application request ---> System.Net.HttpListenerException: The I/O operation has been aborted because of either a thread exit or an application request
at System.Net.HttpRequestStream.EndRead(IAsyncResult asyncResult)
at System.ServiceModel.Channels.DetectEofStream.EndRead(IAsyncResult result)
at System.ServiceModel.Channels.HttpRequestContext.ListenerHttpContext.ListenerContextHttpInput.ListenerContextInputStream.EndRead(IAsyncResult result)
--- End of inner exception stack trace ---
</ExceptionString>
<InnerException>
<ExceptionType>System.Net.HttpListenerException, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>The I/O operation has been aborted because of either a thread exit or an application request</Message>
<StackTrace>
at System.Net.HttpRequestStream.EndRead(IAsyncResult asyncResult)
at System.ServiceModel.Channels.DetectEofStream.EndRead(IAsyncResult result)
at System.ServiceModel.Channels.HttpRequestContext.ListenerHttpContext.ListenerContextHttpInput.ListenerContextInputStream.EndRead(IAsyncResult result)
</StackTrace>
<ExceptionString>System.Net.HttpListenerException (0x80004005): The I/O operation has been aborted because of either a thread exit or an application request
at System.Net.HttpRequestStream.EndRead(IAsyncResult asyncResult)
at System.ServiceModel.Channels.DetectEofStream.EndRead(IAsyncResult result)
at System.ServiceModel.Channels.HttpRequestContext.ListenerHttpContext.ListenerContextHttpInput.ListenerContextInputStream.EndRead(IAsyncResult result)
</ExceptionString>
<NativeErrorCode>3E3</NativeErrorCode>
</InnerException>
</Exception>
Causa
A partir Windows Server 2012 R2, el controlador del kernel que controla las solicitudes HTTP (http.sys) se cambió en términos de cómo controla la Timer_MinBytesPerSecond propiedad. De forma predeterminada, Http.sys considera cualquier velocidad inferior a 150 bytes por segundo como un posible ataque de conexión de baja velocidad y descarta la conexión TCP para liberar el recurso. Este problema no se produce en Windows Server 2008 R2 porque el umbral de una conexión lenta en Windows Server 2012 R2 es mucho más restrictivo.
Solución alternativa
Para evitar esta característica, establezca el valor en 0xFFFFFFFF (valor entero máximo de 32 bits sin signo), que es minSendBytesPerSecond 4.294.967.295 en decimal. Este valor específico deshabilita la característica de conexión de velocidad inferior.
Use uno de los siguientes métodos para establecer el minSendBytesPerSecond valor en 0xFFFFFFFF .
Método 1: Usar un archivo de configuración
<system.net> <settings> <httpListener> <timeouts minSendBytesPerSecond="4294967295" /> </httpListener> </settings> </system.net>Método 2: Establecer mediante programación
Cambie la propiedad explícitamente en el código, como en el ejemplo siguiente:
System.Net.HttpListenerTimeoutManager.MinSendBytesPerSecond = 4294967295
La opción de programación se puede convertir en una opción personalizada si un cambio serviceBehavior de código en el servicio no es una opción. Es decir, un comportamiento se puede integrar con un servicio existente quitando un DLL y cambiando la configuración, como en el ejemplo siguiente:
Abra la solución en Visual Studio y agregue un nuevo proyecto de biblioteca de clases. Así mismo, asigne
BehaviorProjectel nombre .Cree una clase denominada
HttpListenerBehavior.Actualíctelo con el siguiente código fuente:
namespace BehaviorProject { public class HttpListenerBehavior : BehaviorExtensionElement, IServiceBehavior { public override Type BehaviorType { get { return this.GetType(); } } protected override object CreateBehavior() { return new HttpListenerBehavior(); } public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { return; } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { UpdateSystemNetConfiguration(); } private void UpdateSystemNetConfiguration() { ConfigurationProperty minSendBytesPerSecond; minSendBytesPerSecond = new ConfigurationProperty("minSendBytesPerSecond", typeof(long), (long)uint.MaxValue, null, null, ConfigurationPropertyOptions.None); ConfigurationPropertyCollection properties; HttpListenerTimeoutsElement timeOuts = new HttpListenerTimeoutsElement(); properties = GetMember(timeOuts, "properties") as ConfigurationPropertyCollection; if (properties != null) { properties.Remove("minSendBytesPerSecond"); SetMember(timeOuts, "minSendBytesPerSecond", minSendBytesPerSecond); properties.Add(minSendBytesPerSecond); } } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { return; } public static object GetMember(object Source, string Field) { string[] fields = Field.Split('.'); object curr = Source; BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; bool succeeded = false; foreach (string field in fields) { Type t = curr.GetType(); succeeded = false; FieldInfo fInfo = t.GetField(field, bindingFlags); if (fInfo != null) { curr = fInfo.GetValue(curr); succeeded = true; continue; } PropertyInfo pInfo = t.GetProperty(field, bindingFlags); if (pInfo != null) { curr = pInfo.GetValue(curr, null); succeeded = true; continue; } throw new System.IndexOutOfRangeException(); } if (succeeded) return curr; throw new System.ArgumentNullException(); } public static void SetMember(object Source, string Field, object Value) { string[] fields = Field.Split('.'); object curr = Source; BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public; bool succeeded = false; int i = 0; foreach (string field in fields) { i++; Type t = curr.GetType(); succeeded = false; FieldInfo fInfo = t.GetField(field, bindingFlags); if (fInfo != null) { if (i == fields.Length) fInfo.SetValue(curr, Value); curr = fInfo.GetValue(curr); succeeded = true; continue; } PropertyInfo pInfo = t.GetProperty(field, bindingFlags); if (pInfo != null) { if (i == fields.Length) fInfo.SetValue(curr, Value); curr = pInfo.GetValue(curr, null); succeeded = true; continue; } throw new System.IndexOutOfRangeException(); } if (succeeded) return; throw new System.ArgumentNullException(); } } }Cree la aplicación de biblioteca.
Copie la DLL generada en la carpeta de la aplicación.
Abra el archivo de configuración de la aplicación, busque la
<system.serviceModel>etiqueta y agregue el siguiente comportamiento personalizado:<extensions> <behaviorExtensions> <add name="httpListenerBehavior" type="BehaviorProject.HttpListenerBehavior, BehaviorProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> </behaviorExtensions> </extensions> <behaviors> <serviceBehaviors> <!-- if the serviceBehavior used by the service is named, add to the appropriate named behavior --> <behavior name="customBehavior"> <!-- The behavior is referenced by the following in line. Visual Studio will mark this line with a red underline because it is not in the config schema. It can be ignored. Notice that the other behaviors (like serviceMetadata) does not need to be added if they are not currently present --> <httpListenerBehavior /> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors>
Más información
Para determinar si los cambios fueron efectivos, use uno de los métodos siguientes.
Método 1
Capture un archivo de volcado de memoria después de abrir el serviceHost.
Vuelque el objeto de
System.Net.HttpListenerTimeoutManagertipo y lea laminSendBytesPerSecondpropiedad.0:000> !DumpObj /d 02694a64 Name: System.Net.HttpListenerTimeoutManager MethodTable: 7308b070 EEClass: 72ec5238 Size: 20(0x14) bytes File: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll Fields: MT Field Offset Type VT Attr Value Name 73092254 4001605 4 ....Net.HttpListener 0 instance 026932f0 listener 73c755d4 4001606 8 System.Int32[] 0 instance 02694a78 timeouts 73c7ef20 4001607 c System.UInt32 1 instance 4294967295 minSendBytesPerSecond <-----------------Nota
El valor de
minSendBytesPerSecondes 4294967295.
Método 2
En modo administrador, abra cmd.exe y ejecute el siguiente comando desde un símbolo del sistema (después de abrir serviceHost):
netsh http show servicestate view="session" >%temp%\netshOutput.txtEjecute el siguiente comando para abrir el netshOutput.txt archivo. Se abrirá en Bloc de notas.
start %temp%\netshOutput.txtBusque el número de puerto de la aplicación de servicio (como 8888) y, a continuación, vea la sesión. Este es el paso para comprobar que la velocidad de envío mínima (bytes/s) se invalida con el valor 4294967295 segundo.
Debería ver una entrada similar a la siguiente:
Server session ID: FE00000320000021 Version: 2.0 State: Active Properties: Max bandwidth: 4294967295 Timeouts: Entity body timeout (secs): 120 Drain entity body timeout (secs): 120 Request queue timeout (secs): 120 Idle connection timeout (secs): 120 Header wait timeout (secs): 120 Minimum send rate (bytes/sec): 150 URL groups: URL group ID: FD00000340000001 State: Active Request queue name: Request queue is unnamed. Properties: Max bandwidth: inherited Max connections: inherited Timeouts: Entity body timeout (secs): 0 Drain entity body timeout (secs): 0 Request queue timeout (secs): 0 Idle connection timeout (secs): 0 Header wait timeout (secs): 0 Minimum send rate (bytes/sec): 4294967295 <------------------- Number of registered URLs: 1 Registered URLs: HTTP://+:8888/TESTSERVICE1/
Para obtener más información, vea la propiedad HttpListenerTimeoutManager.MinSendBytesPerSecond.