Po poszukiwaniach w całym internecie, znalazłem następujące artykuły firmy Microsoft, które odnoszą się do problemu:
KB 821268: Contention, poor performance, and deadlocks when you make Web service requests from ASP.NET applications
Ten artykuł zawiera wskazówki dotyczące ulepszania wydajności, ale nie wspomina o niektórych BARDZO ważnych pułapach, w których działaliśmy.
Rozwiązaniem dla nas było zmodyfikować naszą Machine.config i wypełnić następujące węzły 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>
ja celowo ustawić niektóre z tych liczb do „XXX”, ponieważ są one uzależnione od posiadanego sprzętu.
Z powyższego artykułu z bazy wiedzy firmy Microsoft Microsoft sugeruje kilka równań do wyznaczenia tych wartości. Jednak oni nie wspomnieć, że maksymalna wartość dla tych liczb jest rozmiar int, lub 32767.
Tak, CORRECT równania zastanawianie się te przedstawiają się następująco:
- maxWorkerThreads: 32767/#Cores
- W naszym przypadku mamy serwer 24-rdzeniowy. Tak, nasza wartość maxWorkerThreads jest prawidłowo ustawiony: 1365 Dowolny numer, z którego wynika całkowitą większą niż 32767, serwer będzie ustawić maxWorkerThreads do 32767.
- maxIoThreads: Tak samo jak maxWorkerThreads (32767/#Cores)
- minWorkerThreads: maxWorkerThreads/2
- było to kłopotliwe. Jeśli przekroczono wartość całkowitą WIĘKSZĄ niż 32767 (i pomimo tego, co mówi artykuł KB, liczba ta jest pomnożona przez liczbę rdzeni), w przeciwieństwie do wartości "max", , to domyślna liczba rdzeni na komputerze! W naszym przypadku było to ustawione na 24 (ponieważ ustawiliśmy arbitralnie wysoką wartość dla min), a to zabijało wydajność na naszym serwerze.
- minIoThreads: Takie same jak minWorkerThreads
- minFreeThreads: 88 * #Cores (pobrane bezpośrednio z artykułu KB)
- minLocalRequestFreeThreads: 76 * #Cores (pobrane bezpośrednio z artykułu KB)
To rozwiązanie nie jest dla wszystkich i powinno być używane tylko wtedy, gdy spełniasz kryteria w artykule KB.
Innym narzędziem, za pomocą którego pomogliśmy nam zdiagnozować to była strona .ASPX bez kodu źródłowego, którą moglibyśmy wyrzucić na dowolnym serwerze (bez resetowania puli aplikacji). Ta strona używa refleksji, aby poinformować Cię, co faktycznie dzieje się w puli wątków i jakie wartości tych ustawień są renderowane na twoim serwerze.
<%@ 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>
Dlaczego maxWorkerThreads byłby odwrotnie proporcjonalny do #Cores? tj. im więcej rdzeni ma maxWorkerThreads jest niższy. Bardziej sensowne jest dla mnie, że maxWorkerThreads to x * #Cores, np. MaxWorkerThreads = 16 * #Cores. – DavidF
Na ile jesteś pewien, że minWorkerThreads i minIoThreads są mnożone przez liczbę rdzeni? Czy to specjalnie dla .net 4.5? –
Pozornie potwierdzone tutaj - https://msdn.microsoft.com/en-us/library/system.web.configuration.processmodelsection.minworkerthreads(v=vs.100).aspx –