在网上搜索后,我找到了以下来自微软的与该问题相关的文章:
知识库 821268:Contention, poor performance, and deadlocks when you make Web service requests from ASP.NET applications
这篇文章提供了一些很棒的性能调整技巧,但是它没有提到我们遇到的一些非常重要的上限。
我们的解决方案是修改我们的 machine.config,并填充以下 XML 节点:
<system.web>
<processModel autoConfig="false" maxWorkerThreads="xxx" maxIoThreads="xxx" minWorkerThreads="xxx" minIoThreads="xxx" requestQueueLimit="5000" responseDeadlockInterval="00:03:00"/>
<httpRuntime minFreeThreads="xxx" minLocalRequestFreeThreads="xxx"/>
</system.web>
我故意将其中一些数字设置为“xxx”,因为它们取决于您的硬件。
从上面的知识库文章中,Microsoft 提出了一些计算这些值的方程式。但是,他们没有提到这些数字的最大值是 INT 的大小,即 32767。
因此,计算出这些的正确方程式如下:
-
maxWorkerThreads:32767 / #Cores
- 在我们的例子中,我们有一个 24 核服务器。因此,我们的 maxWorkerThreads 值正确设置为:1365。任何导致整数大于 32767 的数字,服务器都会将 maxWorkerThreads 设置为 32767。
-
maxIoThreads:与 maxWorkerThreads (32767 / #Cores) 相同
-
minWorkerThreads:maxWorkerThreads / 2
- 这是一个棘手的问题。如果超过一个大于 32767 的整数值(尽管知识库文章说,这个数字乘以您拥有的核心数),并且与“最大值”值不同,这默认为核心数你的机器!在我们的例子中,它被设置为 24(因为我们为最小值设置了一个任意高的值),这会导致我们服务器的性能下降。
-
minIoThreads:与 minWorkerThreads 相同
-
minFreeThreads:88 * #Cores(直接取自知识库文章)
-
minLocalRequestFreeThreads:76 * #Cores(直接取自知识库文章)
此解决方案并不适合所有人,只有在您满足知识库文章中的条件时才应使用。
我们用来帮助我们诊断此问题的另一个工具是一个没有代码隐藏的 .ASPX 页面,我们可以在任何服务器上丢弃它(无需重置应用程序池)。此页面使用反射来告诉您线程池中实际发生了什么,以及这些设置的值在您的服务器上呈现什么。
<%@ Page Language="C#" %>
<!DOCTYPE html>
<html lang="en">
<head>
<style>
body { margin: 20pt; padding: 0pt; font-family: Verdana, "san-serif";}
fieldset { border-radius: 5px; border: none; background-color: #fff; margin: 10pt;}
fieldset.parent { background-color: #f0f0f0; }
legend { font-size: 10pt; color: #888; margin: 5pt; }
.ports div { padding: 10pt 0pt 0pt 0pt; clear: both; }
.ports div:first-child { padding: 0pt; }
.ports div div { padding: 0pt; clear: none; margin: 1pt; background-color: #eef; display: block; float: left; border: 5pt solid #eef; }
.ports div div:first-child { border-top-left-radius: 5pt; border-bottom-left-radius: 5pt; background-color: #ccf; border-color: #ccf;}
.ports div div:last-child { border-top-right-radius: 5pt; border-bottom-right-radius: 5pt; background-color: #ccf; border-color: #ccf; padding: 0pt 10pt 0pt 10pt; }
</style>
</head>
<body>
<%
Response.Cache.SetCacheability(HttpCacheability.NoCache);
int worker, workerMIN, workerMAX;
int port, portMIN, portMAX;
System.Threading.ThreadPool.GetAvailableThreads(out worker, out port);
System.Threading.ThreadPool.GetMinThreads(out workerMIN, out portMIN);
System.Threading.ThreadPool.GetMaxThreads(out workerMAX, out portMAX);
%>
<fieldset class="parent">
<legend>Thread Information</legend>
<fieldset>
<legend>Worker Threads</legend>
<div class="ports">
<div>
<div>Min: <%=workerMIN %></div>
<div>Current: <%=workerMAX - worker %></div>
<div>Max: <%=workerMAX %></div>
</div>
</div>
</fieldset>
<fieldset>
<legend>Completion Port Threads</legend>
<div class="ports">
<div>
<div>Min: <%=portMIN %></div>
<div>Current: <%=portMAX - port %></div>
<div>Max: <%=portMAX %></div>
</div>
</div>
</fieldset>
<fieldset>
<legend>Request Queue Information</legend>
<div class="ports">
<%
var fi = typeof(HttpRuntime).GetField("_theRuntime", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static).GetValue(null);
var rq = typeof(HttpRuntime).GetField("_requestQueue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(fi);
var fields = rq.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (var field in fields)
{
string name = field.Name;
string value = "";
switch (name)
{
case "_localQueue":
case "_externQueue":
System.Collections.Queue queue = field.GetValue(rq) as System.Collections.Queue;
value = queue.Count.ToString();
break;
default:
value = field.GetValue(rq).ToString();
break;
}
%>
<div>
<div><%=name %></div>
<div><%=value %></div>
</div>
<%
//Response.Write(string.Format("{0}={1}<br/>", name, value));
}
%>
</div>
</fieldset>
</fieldset>
</body></html>