Compartir a través de


Problemas de rendimiento al realizar llamadas a servicios web desde una aplicación de ASP.NET

En este artículo se proporciona ayuda para resolver los problemas de rendimiento que se producen al realizar llamadas a servicios web desde una aplicación de Microsoft ASP.NET.

Versión original del producto: ASP.NET
Número de KB original: 821268

Síntomas

Al realizar llamadas a servicios web desde una aplicación de ASP.NET, puede experimentar contención, un rendimiento deficiente y interbloqueos. Los clientes pueden notificar que las solicitudes dejan de responder o tardan mucho tiempo en ejecutarse. Si se sospecha un interbloqueo, se puede reciclar el proceso de trabajo.

Puede recibir el siguiente mensaje de error de excepción al realizar una llamada al HttpWebRequest.GetResponse método :

"System.InvalidOperationException: no había suficientes subprocesos libres en el objeto ThreadPool para completar la operación".

También puede recibir el siguiente mensaje de error de excepción en el explorador:

"HttpException (0x80004005): se agota el tiempo de espera de la solicitud".

Nota

Este artículo también se aplica a las aplicaciones que realizan HttpWebRequest solicitudes directamente.

Causa

Este problema puede producirse porque ASP.NET limita el número de subprocesos de trabajo y subprocesos de puerto de finalización que una llamada puede usar para ejecutar solicitudes.

Normalmente, una llamada a un servicio web usa un subproceso de trabajo para ejecutar el código que envía la solicitud y un subproceso de puerto de finalización para recibir la devolución de llamada del servicio web. Sin embargo, si la solicitud se redirige o requiere autenticación, la llamada puede usar hasta dos subprocesos de trabajo y dos subprocesos de puerto de finalización. Por lo tanto, puede agotar el administrado ThreadPool cuando se producen varias llamadas de servicio web al mismo tiempo.

Por ejemplo, supongamos que ThreadPool está limitado a 10 subprocesos de trabajo y que los 10 subprocesos de trabajo están ejecutando código que está esperando a que se ejecute una devolución de llamada. La devolución de llamada nunca se puede ejecutar, ya que los elementos de trabajo que se ponen en cola a ThreadPool se bloquean hasta que un subproceso esté disponible.

Otro posible origen de contención es el maxconnection parámetro que usa el System.Net espacio de nombres para limitar el número de conexiones. Por lo general, este límite funciona según lo previsto. Sin embargo, si muchas aplicaciones intentan realizar muchas solicitudes a una sola dirección IP al mismo tiempo, es posible que los subprocesos tengan que esperar una conexión disponible.

Solución

Para resolver estos problemas, puede ajustar los parámetros siguientes en el archivo Machine.config para adaptarse mejor a su situación:

  • maxWorkerThreads
  • minWorkerThreads
  • maxIoThreads
  • minFreeThreads
  • minLocalRequestFreeThreads
  • maxconnection
  • executionTimeout

Para resolver correctamente estos problemas, realice las siguientes acciones:

  • Limite el número de solicitudes de ASP.NET que se pueden ejecutar al mismo tiempo a aproximadamente 12 por CPU.
  • Permita que las devoluciones de llamada del servicio web usen libremente subprocesos en .ThreadPool
  • Seleccione un valor adecuado para el maxconnections parámetro . Base la selección en el número de direcciones IP y AppDomains que se usan.

Nota

La recomendación para limitar el número de solicitudes de ASP.NET a 12 por CPU es un poco arbitraria. Sin embargo, este límite ha demostrado funcionar bien para la mayoría de las aplicaciones.

MaxWorkerThreads y maxIoThreads

ASP.NET usa los dos valores de configuración siguientes para limitar el número máximo de subprocesos de trabajo y subprocesos de finalización que se usan:

<processModel maxWorkerThreads="20" maxIoThreads="20">

El maxWorkerThreads parámetro y el maxIoThreads parámetro se multiplican implícitamente por el número de CPU. Por ejemplo, si tiene dos procesadores, el número máximo de subprocesos de trabajo es 2 * maxWorkerThreads.

MinFreeThreads y minLocalRequestFreeThreads

ASP.NET también contiene las siguientes opciones de configuración que determinan cuántos subprocesos de trabajo y subprocesos de puerto de finalización deben estar disponibles para iniciar una solicitud remota o una solicitud local:

<httpRuntime minFreeThreads="8" minLocalRequestFreeThreads="8">

Si no hay suficientes subprocesos disponibles, la solicitud se pone en cola hasta que haya suficientes subprocesos libres para realizar la solicitud. Por lo tanto, ASP.NET no se ejecutarán más que el siguiente número de solicitudes al mismo tiempo:

(maxWorkerThreads * número de CPU) - minFreeThreads

Nota

El minFreeThreads parámetro y el minLocalRequestFreeThreads parámetro no se multiplican implícitamente por el número de CPU.

MinWorkerThreads

ASP.NET también contiene la siguiente configuración que determina cuántos subprocesos de trabajo se pueden poner a disposición inmediatamente para atender una solicitud remota.

<processModel minWorkerThreads="1">

Los subprocesos controlados por esta configuración se pueden crear a una velocidad mucho más rápida que los subprocesos de trabajo que se crean a partir de las funcionalidades predeterminadas de ajuste de subprocesos de Common Language Runtime (CLR).

Esta configuración permite ASP.NET a las solicitudes de servicio que pueden rellenar repentinamente la cola de solicitudes de ASP.NET debido a una ralentización en un servidor back-end, una ráfaga repentina de solicitudes del extremo del cliente o algo similar que provocaría un aumento repentino en el número de solicitudes de la cola.

El valor predeterminado del minWorkerThreads parámetro es 1. Se recomienda establecer el valor del minWorkerThreads parámetro en el siguiente valor:

minWorkerThreads = maxWorkerThreads / 2

De forma predeterminada, el minWorkerThreads parámetro no está presente en el archivo Web.config ni en el archivo Machine.config . Esta configuración se multiplica implícitamente por el número de CPU.

Maxconnection

El maxconnection parámetro determina cuántas conexiones se pueden realizar a una dirección IP específica. El parámetro aparece de la siguiente manera:

<connectionManagement>
    <add address="*" maxconnection="2">
    <add address="http://65.53.32.230" maxconnection="12">
</connectionManagement>

Si el código de la aplicación hace referencia a la aplicación por nombre de host en lugar de por dirección IP, el parámetro debe aparecer de la siguiente manera:

<connectionManagement>
    <add address="*" maxconnection="2">
    <add address="http://hostname" maxconnection="12">
</connectionManagement>

Por último, si la aplicación se hospeda en un puerto distinto de 80, el parámetro tiene que incluir el puerto no estándar en la dirección URL, similar al siguiente:

<connectionManagement>
    <add address="*" maxconnection="2">
    <add address="http://hostname:8080" maxconnection="12">
</connectionManagement>

La configuración de los parámetros que se describen anteriormente en este artículo se encuentra en el nivel de proceso. Sin embargo, la maxconnection configuración del parámetro se aplica al nivel AppDomain. De forma predeterminada, dado que esta configuración se aplica al nivel AppDomain, puede crear un máximo de dos conexiones a una dirección IP específica de cada AppDomain en el proceso.

ExecutionTimeout

ASP.NET usa la siguiente configuración para limitar el tiempo de ejecución de la solicitud:

<httpRuntime executionTimeout="90"/>

También puede establecer este límite mediante la Server.ScriptTimeout propiedad .

Nota

Si aumenta el valor del executionTimeout parámetro, es posible que también tenga que modificar la configuración del processModel responseDeadlockInterval parámetro.

Recomendaciones

Es posible que la configuración recomendada en esta sección no funcione para todas las aplicaciones. Sin embargo, la siguiente información adicional puede ayudarle a realizar los ajustes adecuados.

Si va a realizar una llamada de servicio web a una sola dirección IP de cada página ASPX, Microsoft recomienda usar las siguientes opciones de configuración:

  • Establezca los valores del maxWorkerThreads parámetro y el maxIoThreads parámetro en 100.
  • Establezca el valor del maxconnection parámetro en 12*N (donde N es el número de CPU que tiene).
  • Establezca los valores del minFreeThreads parámetro en 88*N y el minLocalRequestFreeThreads parámetro en 76*N.
  • Establezca el valor de minWorkerThreads en 50. Recuerde que minWorkerThreads no está en el archivo de configuración de forma predeterminada. Debe agregarlo.

Algunas de estas recomendaciones implican una fórmula sencilla que implica el número de CPU en un servidor. La variable que representa el número de CPU en las fórmulas es N.

Para esta configuración, si tiene habilitado hyperthreading, debe usar el número de CPU lógicas en lugar del número de CPU físicas. Por ejemplo, si tiene un servidor de cuatro procesadores con hyperthreading habilitado, el valor de N en las fórmulas será 8 en lugar de 4.

Nota

Al usar esta configuración, puede ejecutar un máximo de 12 ASP.NET solicitudes por CPU al mismo tiempo porque 100-88=12. Por lo tanto, hay al menos 88*N subprocesos de trabajo y 88*N subprocesos de puerto de finalización disponibles para otros usos (como para las devoluciones de llamada del servicio web).

Por ejemplo, tiene un servidor con cuatro procesadores e hyperthreading habilitados. En función de estas fórmulas, usaría los siguientes valores para las opciones de configuración que se mencionan en este artículo.

<system.web>
    <processModel maxWorkerThreads="100" maxIoThreads="100" minWorkerThreads="50"/>
    <httpRuntime minFreeThreads="704" minLocalRequestFreeThreads="608"/>
</system.web>
<system.net>
    <connectionManagement>
        <add address="[ProvideIPHere]" maxconnection="96"/>
    </connectionManagement>
</system.net>

Además, cuando se usa esta configuración, hay 12 conexiones disponibles por CPU por dirección IP para cada AppDomain. Por lo tanto, en el escenario siguiente, se produce muy poca contención cuando las solicitudes están esperando conexiones y ThreadPool no se agota:

  • La web hospeda solo una aplicación (AppDomain).
  • Cada solicitud de una página ASPX realiza una solicitud de servicio web.
  • Todas las solicitudes están en la misma dirección IP.

Sin embargo, cuando se usa esta configuración, los escenarios que implican una de las siguientes probablemente usarán demasiadas conexiones:

  • Las solicitudes son para varias direcciones IP.
  • Las solicitudes se redirigen (código de estado 302).
  • Las solicitudes requieren autenticación.
  • Las solicitudes se realizan desde varios appDomains.

En estos escenarios, es recomendable usar un valor inferior para el maxconnection parámetro y valores superiores para el minFreeThreads parámetro y el minLocalRequestFreeThreads parámetro .

Más información

Para obtener más información, consulte Mejora del rendimiento de ASP.NET.

Si experimenta un rendimiento y una contención deficientes en IIS junto con ASP.NET, vaya a los siguientes blogs de Microsoft: