web-dev-qa-db-fra.com

Pourquoi les threads IIS sont-ils si précieux par rapport aux threads CLR classiques?

Je suis lecture sur AsyncControllers dans ASP.NET MVC.

Il semble que la seule raison pour laquelle ils existent est que les threads IIS peuvent être enregistrés tandis que le travail de longue durée est délégué aux threads CLR normaux, qui semblent être moins chers.

J'ai quelques questions ici:

  • Pourquoi ces threads IIS sont-ils si chers pour justifier toute cette architecture conçue pour prendre en charge les contrôleurs asynchrones?
  • Comment savoir/configurer le nombre de threads IIS exécutés dans mon pool d'applications IIS?
43
André Pena

ASP.NET traite les demandes à l'aide de threads du pool de threads .NET. Le pool de threads conserve un pool de threads qui ont déjà encouru les coûts d'initialisation des threads. Par conséquent, ces fils sont faciles à réutiliser. Le pool de threads .NET s'auto-ajuste également. Il surveille l'utilisation du processeur et d'autres ressources et ajoute de nouveaux threads ou ajuste la taille du pool de threads selon les besoins. Vous devez généralement éviter de créer des threads manuellement pour effectuer le travail. Utilisez plutôt des threads du pool de threads. Dans le même temps, il est important de s'assurer que votre application n'effectue pas de longues opérations de blocage qui pourraient rapidement entraîner une famine du pool de threads et rejeter les requêtes HTTP.

Les E/S disque, les appels de service Web, sont tous bloquants. Il est préférable d'optimiser l'utilisation d'appels asynchrones. Lorsque vous effectuez un appel asynchrone, asp.net libère votre thread et la demande sera affectée à un autre thread lorsque la fonction de rappel est invoquée.

Pour configurer le nombre de threads, vous pouvez définir:

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

Reportez-vous: tilisation du thread ASP.NET sur IIS 7.5, IIS 7.0 et IIS 6. =

Ce sont les paramètres qui les meilleures pratiques Microsoft recommandent :

  • Définissez maxconnection sur 12 * # de CPU . Ce paramètre contrôle le nombre maximal de connexions HTTP sortantes que vous pouvez initier à partir d'un client. Dans ce cas, ASP.NET est le client. Définissez maxconnection sur 12 * # de CPU.
  • Définissez maxIoThreads à 100 . Ce paramètre contrôle le nombre maximal de threads d'E/S dans le pool de threads .NET. Ce nombre est automatiquement multiplié par le nombre de CPU disponibles. Définissez maxloThreads sur 100.
  • Définissez maxWorkerThreads sur 100 . Ce paramètre contrôle le nombre maximal de threads de travail dans le pool de threads. Ce nombre est ensuite automatiquement multiplié par le nombre de CPU disponibles. Définissez maxWorkerThreads sur 100.
  • Définissez minFreeThreads sur 88 * # de CPU . Ce paramètre est utilisé par le processus de travail pour mettre en file d'attente toutes les demandes entrantes si le nombre de threads disponibles dans le pool de threads tombe en dessous de la valeur de ce paramètre. Ce paramètre limite efficacement le nombre de demandes pouvant s'exécuter simultanément à maxWorkerThreads - minFreeThreads. Définissez minFreeThreads sur 88 * # de CPU. Cela limite le nombre de demandes simultanées à 12 (en supposant que maxWorkerThreads est de 100).
  • Définissez minLocalRequestFreeThreads sur 76 * # de CPU . Ce paramètre est utilisé par le processus de travail pour mettre en file d'attente les demandes de l'hôte local (où une application Web envoie des demandes à un service Web local) si le nombre de threads disponibles dans le pool de threads est inférieur à ce nombre. Ce paramètre est similaire à minFreeThreads mais il ne s'applique qu'aux requêtes localhost de l'ordinateur local. Définissez minLocalRequestFreeThreads sur 76 * # de CPU.

Remarque : Les recommandations fournies dans cette section ne sont pas des règles. Ils sont un point de départ.

Vous devrez comparer votre application pour trouver ce qui fonctionne le mieux pour votre application.

48
nunespascal

Les threads IIS sont extraits du pool de threads par défaut, qui est limité par défaut en fonction du nombre de cœurs de processeur. Si cette file d'attente du pool de threads est sauvegardée, IIS cessera de répondre aux demandes. En utilisant du code asynchrone, le thread du pool de threads peut être renvoyé au pool pendant que l'opération asynchrone a lieu, permettant à IIS de traiter plus de demandes dans l'ensemble.

D'un autre côté, la création d'un nouveau thread par vous-même n'utilise pas de thread de pool de threads. La génération d'un nombre non contrôlé de threads indépendants peut également être un problème, donc ce n'est pas une solution miracle au problème du pool de threads IIS. Async IO est généralement préféré dans les deux cas.

Quant à la modification du nombre de threads dans le pool de threads, cochez ici . Cependant, vous devriez probablement vraiment éviter de le faire.

6
Chris

Notre Webservice a besoin de temps en temps de servir 100 requets/seconde tandis que le reste du temps c'est 1 requête/seconde. Analazyng IIS logs nous avons découvert qu'il fallait environ 28s quand une rafale se produit pour servir de tels appels.

L'application de meilleures pratiques Microsoft comme cité par @nunespascal a considérablement réduit le temps à 1 s dans notre cas.

Vous trouverez ci-dessous le script Powershell que nous utilisons actuellement lorsque nous déployons nos serveurs de production. Il met à jour machine.config en tenant compte du nombre de processeurs logiques.

<# 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);

Cette solution nous est venue d'un blog article rédigé par Stuart Brierley en 2009. Nous l'avons testé avec succès avec Windows Server de 2008 R2 à 2016.

4

En fait, ce qui est écrit en article que vous avez lié n'est pas vrai. Le modèle asynchrone n'est pas là pour libérer "super coûteux IIS Worker Threads" et utiliser en arrière-plan d'autres "threads bon marché".

Le modèle asynchrone est là simplement pour libérer les threads. Vous pouvez en bénéficier dans des scénarios où vous n'avez pas besoin de vos threads (et mieux même pas de votre machine locale).

Je peux nommer deux exemples de scénarios (les deux E/S):

Premier:

  1. BeginRequest
  2. Commencer la lecture du fichier asynchrone
  3. pendant la lecture du fichier, vous n'avez pas besoin de votre thread - les autres requêtes peuvent donc l'utiliser.
  4. La lecture du fichier se termine - vous obtenez le fil du pool d'applications.
  5. Demande de finitions.

Et seconde presque identique:

  1. BeginRequest
  2. Commencez l'appel asynchrone au service WCF.
  3. nous pouvons quitter notre machine et ne pas avoir besoin de notre thread - donc d'autres requêtes peuvent l'utiliser.
  4. Nous recevons une réponse du service distant - nous obtenons du fil du pool d'applications pour continuer.
  5. Demande de finitions.

Il est généralement sûr de lire msdn. Vous pouvez obtenir des informations sur le modèle asynchrone ici .

4
Grzegorz W