El servicio WCF puede escalarse lentamente bajo carga

Este artículo le ayuda a resolver el error que se produce Windows servicio de Communication Foundation (WCF) puede escalar lentamente bajo carga.

Versión del producto original:   Windows Communication Foundation
Número KB original:   2538826

Síntomas

Cuando el servicio WCF recibe una ráfaga de solicitudes, es posible que el grupo de subprocesos de puerto de finalización de E/S (IOCP) predeterminado de .NET no aumente el tamaño tan rápido como desee y el tiempo de respuesta de WCF aumentará como resultado. Según el tiempo de ejecución y el número de solicitudes recibidas, es posible que observe que el tiempo de ejecución de WCF aumenta linealmente en aproximadamente 500 ms por cada solicitud recibida hasta que el proceso haya creado suficientes subprocesos IOCP para dar servicio a las solicitudes o mantener la carga entrante. El problema es más evidente en los servicios con tiempos de ejecución más largos. El problema de escalabilidad del grupo de subprocesos IOCP no suele observarse en la carga inicial del proceso.

Causa

Tres variables que tienen un impacto en la capacidad del servicio WCF de escalar verticalmente casi a la misma velocidad que las solicitudes entrantes.

  1. Limitación de WCF

  2. Valor clr de .NET Threadpool.GetMinThreads

  3. Error del grupo de subprocesos IOCP de .NET CLR donde los subprocesos IOCP ya no se crean en un patrón correspondiente al volumen de solicitud entrante antes Threadpool.GetMinThreads del valor de limitación.

En este artículo se describe cómo resolver el problema con el grupo de subprocesos IOCP de .NET, #3. Si tiene problemas de limitación debidos a la limitación wcf GetMinThreads o al valor, esta solución no evitará esos límites. Vea la sección Más información a continuación para obtener instrucciones sobre cómo identificar el escenario. El error de creación de subprocesos IOCP debe solucionarse en la próxima versión 4.0 de la .NET Framework. Este problema de escalabilidad no existe en el grupo de subprocesos de trabajo clr de .NET.

Solución

Al mover la ejecución del servicio WCF a otro grupo de subprocesos, puede incurrir en una pequeña sobrecarga al implementar esta solución. Los resultados de rendimiento variarán según el servicio WCF. Pruebe cada servicio WCF para obtener resultados individuales.

Nota

Aplique esta solución al usar un agente de escucha WCF que no bloquee el subproceso entrante mientras espera a que se complete el código de servicio WCF.

Escucha wcf Solución recomendada
Módulo de sincronización HTTP (predeterminado en 3.x): se usa en el grupo de aplicaciones integrado Cambie al controlador Async y, a continuación, aplique la solución de este artículo o use como alternativa un grupo de subprocesos privados. (vea los vínculos que se incluyen a continuación de esta tabla)
Módulo asincrónico HTTP (predeterminado en 4.x): se usa en el grupo de aplicaciones integrado Aplique la solución de código de este artículo.
ISAPI: se usa en el grupo de aplicaciones de modo clásico Aplicar grupo de subprocesos privados. (vea los vínculos que se incluyen a continuación de esta tabla)
tcp.Net Aplique la solución de código de este artículo.

Si no puede aplicar la solución de este artículo después de la tabla anterior, puede encontrar un ejemplo con un grupo de subprocesos privado en un artículo de MSDN: Foundations: Synchronization Contexts in WCF.

Pasos para implementar esta solución que ejecutará el servicio WCF en el grupo de subprocesos de trabajo clr de .NET:

  1. Los umbrales de limitación wcf deberían ser lo suficientemente altos como para controlar el volumen de ráfagas anticipado en tiempos de respuesta aceptables.

  2. Si usa uno de los grupos de subprocesos predeterminados de .NET CLR, Worker o IOCP para el servicio WCF, debe asegurarse de que el número mínimo de subprocesos (valor donde comienza la limitación de creación de subprocesos) a un número que prevé ejecutar simultáneamente.

  3. Implemente el siguiente código en el servicio que, a continuación, ejecutará el servicio WCF en el grupo de subprocesos de trabajo clr de .NET.

    Esta clase se usa para mover la ejecución al grupo de subprocesos de trabajo clr de .NET.

    public class WorkerThreadPoolSynchronizer : SynchronizationContext
    {
        public override void Post(SendOrPostCallback d, object state)
        {
         // WCF almost always uses Post
            ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
        }
    
        public override void Send(SendOrPostCallback d, object state)
        {
         // Only the peer channel in WCF uses Send
            d(state);
        }
    }
    

    A continuación, debemos crear una clase de atributo personalizada.

    [AttributeUsage(AttributeTargets.Class)]
    public class WorkerThreadPoolBehaviorAttribute : Attribute, IContractBehavior
    {
        private static WorkerThreadPoolSynchronizer synchronizer = new WorkerThreadPoolSynchronizer();
    
        void IContractBehavior.AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }
    
        void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }
    
        void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
        {
        dispatchRuntime.SynchronizationContext = synchronizer;
        }
    
        void IContractBehavior.Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
        {
        }
    }
    

    Ahora para aplicar el atributo personalizado al servicio WCF. Ejemplo:

    [WorkerThreadPoolBehavior]
    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            int iSleepSec = (value * 1000);
            System.Threading.Thread.Sleep(iSleepSec);
            return string.Format("You slept for: {0} seconds", value);
        }
    }
    

Más información

WCF usa el grupo de subprocesos IOCP de .NET CLR para ejecutar el código de servicio WCF. El problema se produce cuando el grupo de subprocesos IOCP de .NET CLR entra en un estado en el que no puede crear subprocesos lo suficientemente rápido como para controlar inmediatamente una ráfaga de solicitudes. El tiempo de respuesta aumenta inesperadamente a medida que se crean nuevos subprocesos a una velocidad de 1 por 500 ms.

El problema puede hacerse más evidente si el servicio WCF usa una tecnología que también usa el grupo de subprocesos IOCP de .NET CLR. Por ejemplo, el Windows de caché de AppFabric del servidor aprovecha este grupo de subprocesos en pequeña medida.

Si no está alcanzando los límites de limitación wcf descritos anteriormente, la siguiente información le ayudará a determinar si está experimentando el problema con el grupo de subprocesos IOCP de .NET CLR.

Los grupos de subprocesos CLR de .NET usan un valor para determinar cuándo empezar a limitación la creación de subprocesos. Esta configuración se puede determinar llamando al método ThreadPool.GetMinThreads(Int32, Int32) o al analizar un volcado de proceso mediante el método ! Extensión de depurador SOSSOS.dll (extensión de depuración SOS).

0:000> ! C:\windows\Microsoft.NET\Framework64\v4.0.30319\sos.threadpool
Uso de la CPU: 0%
Subproceso de trabajo: Total: 16 En ejecución: 0 Inactivo: 16 MaxLimit: 250 MinLimit: 125
Solicitud de trabajo en cola: 0
Número de temporizadores: 35
Subproceso de puerto de finalización:Total: 26 Libre: 0 MaxFree: 16 CurrentLimit: 28 MaxLimit: 1000 MinLimit: 125

El problema observado es cuando el grupo de subprocesos IOCP de .NET CLR especifica una condición en la que solo se crea un subproceso nuevo cada 500 ms (dos por segundo) antes del valor MinLimit del grupo de subprocesos para el grupo de subprocesos. Otros factores esperados que también pueden contribuir a un retraso de creación de subprocesos serían la presión de memoria o la CPU alta.

Supervisar el proceso que hospeda el servicio WCF. Si observa un problema al escalar los subprocesos antes de los umbrales mínimos que ha establecido, es posible que encuentre el problema con el grupo de subprocesos IOCP de .NET CLR. Para determinar si este es el caso, el rendimiento debe usarse para supervisar la tasa de creación de subprocesos de proceso en comparación con la tasa de solicitudes entrantes. Para ello, registre o vea los siguientes contadores de rendimiento (a continuación se muestra un ejemplo para un servicio WCF 4.0 hospedado por IIS (WAS) mediante un enlace HTTP):

Contador Instancias
Recuento de procesos/subprocesos Todas las instancias W3WP(x)
Colas de solicitud de servicio HTTP / Tasa de llegada
ASP.NET aplicaciones v(4 o 2) / Solicitudes en ejecución <instancias de aplicación WCF >
ASP.NET aplicaciones v(4 o 2) / Tiempo de ejecución de solicitud <instancias de aplicación WCF >

Puede usar los contadores de rendimiento WCF si también los tiene habilitados:

Contadores de rendimiento WCF

Es normal ver un recuento de subprocesos que aumenta lentamente cuando la tasa de llegada (patrón de solicitudes de cliente) sigue el mismo patrón. Solo cuando hay un pico inmediato de solicitudes entrantes y el recuento de subprocesos aumenta lentamente a una velocidad de dos subprocesos por segundo mientras aumenta el tiempo de respuesta wcf que existe un problema.

Esta captura de pantalla muestra un proceso de trabajo que después de algún tiempo ha encontrado el problema de escalabilidad del grupo de subprocesos IOCP de .NET. Cuando se inició el proceso por primera vez, los subprocesos IOCP normalmente se crean en paralelo a la carga de la solicitud entrante. En este AppPool (W3WP.EXE), se estaban ejecutando dos servicios WCF. Un servicio usaba el grupo de subprocesos IOCP de .NET predeterminado que recibió una ráfaga de 100 solicitudes a las 10:22:14 y de nuevo a las 10:23:34. El segundo servicio WCF estaba usando la solución alternativa anterior para ejecutarse en el grupo de subprocesos de trabajo de .NET y recibió una ráfaga de 100 solicitudes a las 10:22:54. Después de especificar este estado, es necesario reciclar un proceso para restaurar el grupo de subprocesos iocp a un estado escalable y en funcionamiento.

La captura de pantalla muestra el proceso de trabajo después de encontrar la escalabilidad del grupo de subprocesos.