2012-09-06 33 views
35

Estoy reading about AsyncControllers en ASP.NET MVC.¿Por qué los hilos IIS son tan preciados en comparación con los hilos regulares de CLR?

Parece que la única razón por la que existen es para que los subprocesos de IIS se puedan guardar, mientras que el trabajo de larga ejecución se delega en subprocesos regulares de CLR, que parecen ser más baratos.

Tengo un par de preguntas aquí:

  • ¿Por qué son estos hilos IIS tan caro para justificar toda esta arquitectura construida para soportar controladores asíncronos?
  • ¿Cómo sé/configuro cuántos subprocesos de IIS se están ejecutando en mi grupo de aplicaciones de IIS?

Respuesta

37

ASP.NET procesa las solicitudes utilizando subprocesos del grupo de subprocesos de .NET. El grupo de subprocesos mantiene un grupo de subprocesos que ya han incurrido en los costos de inicialización de subprocesos. Por lo tanto, estos hilos son fáciles de reutilizar. El grupo de subprocesos de .NET también se autoajusta. Supervisa la utilización de la CPU y de otros recursos, y agrega nuevos subprocesos o recorta el tamaño del grupo de subprocesos según sea necesario. En general, debe evitar crear subprocesos manualmente para realizar el trabajo. En cambio, use hilos del grupo de subprocesos. Al mismo tiempo, es importante asegurarse de que su aplicación no realice operaciones de bloqueo prolongadas que podrían llevar rápidamente a la inanición del grupo de subprocesos y a las solicitudes HTTP rechazadas.

E/S de disco, llamadas al servicio web, son todas de bloqueo. Se optimizan mejor mediante el uso de llamadas asincrónicas. Cuando realiza una llamada asincrónica, asp.net libera su hilo y la solicitud se asignará a otro hilo cuando se invoque la función de devolución de llamada.

Para configurar el número de hilos se puede establecer:

<system.web> 
    <applicationPool maxConcurrentRequestsPerCPU="50" maxConcurrentThreadsPerCPU="0" requestQueueLimit="5000"/> 
</system.web> 

Consulte: ASP.NET Thread Usage on IIS 7.5, IIS 7.0, and IIS 6.0

Estos son el escenario que Microsoft best practices recommend:

  • Conjunto maxconnection a 12 * # de CPUs. Esta configuración controla la cantidad máxima de conexiones HTTP salientes que puede iniciar desde un cliente. En este caso, ASP.NET es el cliente. Establezca maxconnection a 12 * # de CPU.
  • Establezca maxIoThreads en 100. Esta configuración controla la cantidad máxima de subprocesos de E/S en el grupo de subprocesos de .NET. Este número se multiplica automáticamente por la cantidad de CPU disponibles. Establezca maxloThreads en 100.
  • Establezca maxWorkerThreads en 100. Esta configuración controla el número máximo de subprocesos de trabajo en el grupo de subprocesos. Este número se multiplica automáticamente por la cantidad de CPU disponibles. Establezca maxWorkerThreads en 100.
  • Establezca minFreeThreads en 88 * # de CPU. El proceso de trabajo utiliza esta configuración para poner en cola todas las solicitudes entrantes si el número de subprocesos disponibles en el grupo de subprocesos cae por debajo del valor de esta configuración. Esta configuración limita efectivamente la cantidad de solicitudes que se pueden ejecutar simultáneamente en maxWorkerThreads - minFreeThreads. Establezca minFreeThreads en 88 * # de CPU. Esto limita el número de solicitudes concurrentes a 12 (suponiendo que maxWorkerThreads sea 100).
  • Establezca minLocalRequestFreeThreads en 76 * # de CPU. El proceso de trabajo utiliza esta configuración para poner en cola solicitudes de localhost (donde una aplicación web envía solicitudes a un servicio web local) si la cantidad de subprocesos disponibles en el grupo de subprocesos cae por debajo de este número. Esta configuración es similar a minFreeThreads, pero solo se aplica a las solicitudes del localhost desde la computadora local. Establezca minLocalRequestFreeThreads en 76 * # de CPU.

Nota: Las recomendaciones que se proporcionan en esta sección no son reglas. Ellos son un punto de partida.

Tendría que comparar su aplicación para encontrar lo que funciona mejor para su aplicación.

+1

Tenga cuidado con el valor de configuración de las mejores prácticas de Microsoft, el artículo fue escrito en 2004 (!). Sin embargo, buena respuesta! –

+1

Afinar los números anteriores realmente funciona – jbro91837

6

Los subprocesos IIS se toman del grupo de subprocesos predeterminado, que está limitado de forma predeterminada en función del número de núcleos de procesador. Si esta cola del grupo de subprocesos recibe una copia de seguridad, IIS dejará de responder a las solicitudes. Mediante el uso de un código asíncrono, el subproceso del grupo de subprocesos se puede devolver al grupo mientras se lleva a cabo la operación asincrónica, lo que permite a IIS atender más solicitudes en general.

Por otro lado, el engendrar un nuevo hilo por su cuenta no utiliza un hilo de grupo de subprocesos. Generar un número no verificado de subprocesos independientes también puede ser un problema, por lo que no es una solución definitiva para el problema del grupo de subprocesos de IIS. Async IO generalmente se prefiere de cualquier manera.

En cuanto a cambiar el número de subprocesos en el grupo de subprocesos, check here. Sin embargo, probablemente debería realmente evitar hacerlo.

+0

Incluso si se utiliza código asíncrono el hilo grupo de subprocesos todavía está atado durante la carga del archivo, pero la respuesta (devolución de llamada) no puede usar el mismo hilo. La aplicación podría usar el hilo original para atender una nueva solicitud y su respuesta a la carga del archivo podría ponerse en cola, ¿verdad? –

3

En realidad, lo que está escrito en article you have linked no es verdadero. El patrón asíncrono no está disponible para liberar "subprocesos IIS de trabajo súper costosos" y utilizar en segundo plano algunos otros "hilos baratos".

El patrón asíncrono está allí simplemente para liberar hilos. Puede beneficiarse de ello en escenarios en los que no necesita sus hilos (y mejor aún no en su máquina local).

puedo nombrar dos ejemplos de escenarios (tanto de E/S):

Primero:

  1. BeginRequest
  2. Comience archivo asíncrono leer
  3. durante la lectura del archivo que no es necesario el hilo - para que otras solicitudes puedan usarlo.
  4. La lectura del archivo finaliza: obtiene el hilo del grupo de aplicaciones.
  5. Solicitud de finaliza.

Y casi idéntica segundo:

  1. BeginRequest
  2. Begin llamada asincrónica al servicio WCF.
  3. podemos dejar nuestra máquina y no necesitamos nuestro hilo, por lo que otras solicitudes pueden usarlo.
  4. Recibimos respuesta del servicio remoto: obtenemos algunos hilos del grupo de aplicaciones para continuar.
  5. Solicitud de finaliza.

Por lo general, es seguro leer msdn. Puede obtener información sobre el patrón asincrónico here.

+4

Hola, autor del artículo al que se hace referencia aquí. Hay dos maneras de una operación aysnchronous se puede realizar en cualquier sistema: * E/S de los puertos de finalización (que es como Node.js obras) * La descarga de tareas de larga ejecución en otros hilos (paralelismo) Lo que era No estaba claro en el momento en que escribí ese artículo (hace un año y medio) cómo funcionaban AsyncControllers bajo el capó. Toda la literatura que encontré apuntó a la opción # 2: que todo lo que hizo AsnycController fue entregar su solicitud de larga ejecución a un hilo CLR (un hilo "verde" barato) y esperar una devolución de llamada antes de unirse a un trabajador de IIS hilo. – Aaronontheweb

+0

Sin poder ver el origen de AspNetSynchronizationContext, es difícil ver exactamente qué método se usa. Los documentos actualizados de MSDN hacen que suene como puertos de finalización de E/S, lo que tiene sentido dado el énfasis en la asincronización que Microsoft ha tenido en los 18 meses desde que escribí ese artículo. Si le interesa, aquí está la fuente de cómo se invocan los métodos de acción en un AsyncController en MVC4: http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/4ed8b6e9c8d1#src%2fSystem.Web.Mvc%2fAsync % 2fAsyncControllerActionInvoker.cs – Aaronontheweb

1

Nuestro servicio web necesita de vez en cuando para atender 100 requets/second mientras que el resto del tiempo es 1 solicitud/segundo. En los registros de Analazyng IIS descubrimos que tomó alrededor de 28s cuando se produce una ráfaga para atender tales llamadas.

Aplicando Microsoft best practices según lo citado por @nunespascal redujo drásticamente el tiempo a 1s en nuestro caso.

A continuación se muestra una secuencia de comandos de Powershell que utilizamos actualmente cuando implementamos nuestros servidores de producción. Actualiza machine.config tomando el recuento del número lógico del procesador.

<# Get and backup current machine.config #> 
$path = "C:\Windows\Microsoft.Net\Framework\v4.0.30319\Config\machine.config"; 
$xml = [xml] (get-content($path)); 
$xml.Save($path + "-" + (Get-Date -Format "yyyyMMdd-HHmm") + ".bak"); 

<# Get number of physical CPU #> 
$physicalCPUs = ([ARRAY](Get-WmiObject Win32_Processor)).Count; 

<# Get number of logical processors #> 
$logicalProcessors = (([ARRAY](Get-WmiObject Win32_Processor))[0] | Select-Object “numberOfLogicalProcessors").numberOfLogicalProcessors * $physicalCPUs; 

<# Set Number of connection in system.net/connectionManagement #> 
$systemNet = $xml.configuration["system.net"]; 
if (-not $systemNet){ 
    $systemNet = $xml.configuration.AppendChild($xml.CreateElement("system.net")); 
} 

$connectionManagement = $systemNet.connectionManagement; 
if (-not $connectionManagement){ 

    $connectionManagement = $systemNet.AppendChild($xml.CreateElement("connectionManagement")); 
} 

$add = $connectionManagement.add; 
if(-not $add){ 
    $add = $connectionManagement.AppendChild($xml.CreateElement("add")) ; 
} 
$add.SetAttribute("address","*"); 
$add.SetAttribute("maxconnection", [string]($logicalProcessors * 12)); 

<# Set several thread settings in system.web/processModel #> 
$systemWeb = $xml.configuration["system.web"]; 
if (-not $systemWeb){ 
    $systemWeb = $xml.configuration.AppendChild($xml.CreateElement("system.web")); 
} 

$processModel = $systemWeb.processModel; 
if (-not $processModel){ 
    $processModel = $systemWeb.AppendChild($xml.CreateElement("processModel")); 
} 
$processModel.SetAttribute("autoConfig","true"); 
$processModel.SetAttribute("maxWorkerThreads","100"); 
$processModel.SetAttribute("maxIoThreads","100"); 
$processModel.SetAttribute("minWorkerThreads","50"); 
$processModel.SetAttribute("minIoThreads","50"); 

<# Set other thread settings in system.web/httRuntime #> 
$httpRuntime = $systemWeb.httpRuntime; 
if(-not $httpRuntime){ 
    $httpRuntime = $systemWeb.AppendChild($xml.CreateElement("httpRuntime")); 
} 
$httpRuntime.SetAttribute("minFreeThreads",[string]($logicalProcessors * 88)); 
$httpRuntime.SetAttribute("minLocalRequestFreeThreads",[string]($logicalProcessors * 76)); 

<#Save modified machine.config#> 
$xml.Save($path); 

Esta solución nos llegó de un blog article witten by Stuart Brierley en 2009. Nos mostrará cuando probamos con Windows Server 2008 R2 a partir de 2016.

Cuestiones relacionadas