48

我正在阅读ASP.NET MVC 中的 AsyncControllers。

似乎它们存在的唯一原因是可以保存 IIS 线程,而将长时间运行的工作委托给常规 CLR 线程,这似乎更便宜。

我在这里有几个问题:

  • 为什么这些 IIS 线程如此昂贵,以证明为支持异步控制器而构建的整个体系结构是合理的?
  • 我如何知道/配置在我的 IIS 应用程序池中运行了多少个 IIS 线程?
4

4 回答 4

53

ASP.NET processes requests by using threads from the .NET thread pool. The thread pool maintains a pool of threads that have already incurred the thread initialization costs. Therefore, these threads are easy to reuse. The .NET thread pool is also self-tuning. It monitors CPU and other resource utilization, and it adds new threads or trims the thread pool size as needed. You should generally avoid creating threads manually to perform work. Instead, use threads from the thread pool. At the same time, it is important to ensure that your application does not perform lengthy blocking operations that could quickly lead to thread pool starvation and rejected HTTP requests.

Disk I/O, web service calls, are all blocking. There are best optimized by using async calls. When you make an async call, asp.net frees your thread and the request will be assigned to another thread when the callback function is invoked.

To configure the number of threads you can set:

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

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

These are the setting that Microsoft best practices recommend:

  • Set maxconnection to 12 * # of CPUs. This setting controls the maximum number of outgoing HTTP connections that you can initiate from a client. In this case, ASP.NET is the client. Set maxconnection to 12 * # of CPUs.
  • Set maxIoThreads to 100. This setting controls the maximum number of I/O threads in the .NET thread pool. This number is automatically multiplied by the number of available CPUs. Set maxloThreads to 100.
  • Set maxWorkerThreads to 100. This setting controls the maximum number of worker threads in the thread pool. This number is then automatically multiplied by the number of available CPUs. Set maxWorkerThreads to 100.
  • Set minFreeThreads to 88 * # of CPUs. This setting is used by the worker process to queue all the incoming requests if the number of available threads in the thread pool falls below the value for this setting. This setting effectively limits the number of requests that can run concurrently to maxWorkerThreads - minFreeThreads. Set minFreeThreads to 88 * # of CPUs. This limits the number of concurrent requests to 12 (assuming maxWorkerThreads is 100).
  • Set minLocalRequestFreeThreads to 76 * # of CPUs. This setting is used by the worker process to queue requests from localhost (where a Web application sends requests to a local Web service) if the number of available threads in the thread pool falls below this number. This setting is similar to minFreeThreads but it only applies to localhost requests from the local computer. Set minLocalRequestFreeThreads to 76 * # of CPUs.

Note: The recommendations that are provided in this section are not rules. They are a starting point.

You would have to benchmark your application to find what works best for your application.

于 2012-09-06T18:10:37.130 回答
6

IIS 线程取自默认线程池,默认情况下受处理器内核数量限制。如果此线程池队列被备份,IIS 将停止响应请求。通过使用异步代码,可以在异步操作发生时将线程池线程返回到池中,从而使 IIS 能够为更多请求提供整体服务。

另一方面,自己生成一个新线程并不使用线程池线程。生成未经检查的独立线程数量也可能是一个问题,因此它不是解决 IIS 线程池问题的所有方法。无论哪种方式,通常都首选异步 IO。

至于更改线程池中的线程数,请查看此处。但是,您可能真的应该避免这样做。

于 2012-09-06T17:00:00.507 回答
5

我们的 Web 服务需要不时地提供100 个请求/秒,而其余时间为 1 个请求/秒。分析 IIS 日志,我们发现在发生突发事件时为此类调用提供服务大约需要28秒。

在我们的案例中,应用@nunespascal引用的Microsoft 最佳实践可将时间大幅缩短至 1 秒

下面是我们目前在部署生产服务器时使用的 Powershell 脚本。它更新 machine.config 以计算逻辑处理器编号。

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

这个解决方案来自Stuart Brierley在 2009 年撰写的一篇博客文章。我们从 2008 R2 到 2016 年成功地使用 Windows Server 对其进行了测试。

于 2017-05-17T09:57:48.280 回答
4

实际上,您链接的文章中所写的内容是不正确的。异步模式不能释放“超级昂贵的 IIS 工作线程”并在后台使用其他一些“廉价线程”。

异步模式只是为了释放线程。在不需要线程(最好甚至不需要本地机器)的情况下,您可以从中受益。

我可以举出两个示例场景(都是 I/O):

第一的:

  1. 开始请求
  2. 开始异步文件读取
  3. 在文件读取期间,您不需要您的线程 - 因此其他请求可以使用它。
  4. 文件读取结束 - 您从应用程序池中获取线程。
  5. 请求完成。

几乎相同的第二个:

  1. 开始请求
  2. 开始异步调用 WCF 服务。
  3. 我们可以离开我们的机器并且不需要我们的线程——所以其他请求可以使用它。
  4. 我们从远程服务获得响应——我们从应用程序池中获得一些线程以继续。
  5. 请求完成。

阅读 msdn 通常是安全的。您可以在此处获取有关异步模式的信息。

于 2012-09-06T18:57:56.020 回答