Server 2008 RC2, IIS 7.5, ASP.NET и запросы в очереди с низкой производительностью

Я уже знаю ответ на этот вопрос, но хочу поделиться с сообществом, так как он НЕ зарегистрирован из Microsoft.

Сценарий: всплеск трафика попадает на ваш веб-сайт IIS 7.5 ASP.NET, и вы замечаете, что запросы запускаются в очереди. Производительность сайта замедляется до обхода, но у вас есть много доступных CPU и RAM.

Это проблема, которую мы недавно увидели на сайте, который сделал кучу внутренних вызовов веб-сервиса. Внутренняя проверка работоспособности запустит тайм-аут, из-за чего этот сервер выйдет из нашего кластера. (Тем не менее, этот сервер является самым мощным оборудованием группы...)

Ответ 1

После поиска в Интернете я нашел следующие статьи от Microsoft, которые относятся к проблеме:

KB 821268: Конфликт, низкая производительность и взаимоблокировки при выполнении запросов веб-сервисов из приложений ASP.NET

В этой статье приводятся некоторые полезные советы по настройке производительности, однако не упоминается несколько ОЧЕНЬ важных потолков, в которые мы столкнулись.

Решение для нас состояло в том, чтобы изменить наш файл 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", так как они зависят от вашего оборудования.

Из статьи KB выше, Microsoft предлагает некоторые уравнения для определения этих значений. Однако они не упоминают, что значение MAXIMUM для этих чисел - это размер INT или 32767.

Итак, уравнения ПРАВИЛЬНЫЕ для их определения выглядят следующим образом:

  • maxWorkerThreads: 32767/#Cores
    • В нашем случае у нас есть 24-ядерный сервер. Итак, наше значение maxWorkerThreads правильно установлено: 1365. Любое число, которое приводит к целому LARGER, чем 32767, сервер установит maxWorkerThreads на 32767.
  • maxIoThreads: То же, что и maxWorkerThreads (32767/#Cores)
  • minWorkerThreads: maxWorkerThreads/2
    • Это было сложно. Если одно превысило целочисленное значение LARGER, чем 32767 (и, несмотря на то, что сказано в статье в KB, это число IS умножается на количество ядер, которые у вас есть), и в отличие от значения "max" это значение по умолчанию соответствует количеству ядер на вашем компьютере! В нашем случае это устанавливалось равным 24 (потому что мы установили произвольно высокое значение для min), и это было УБЫТЬ производительность на нашем сервере.
  • minIoThreads: То же, что и minWorkerThreads
  • minFreeThreads: 88 * #Cores (взято непосредственно из статьи KB)
  • minLocalRequestFreeThreads: 76 * #Cores (взято непосредственно из статьи KB)

Это решение не для всех и должно использоваться только в том случае, если вы отвечаете критериям в статье базы знаний.

Еще один инструмент, который мы использовали, помогая нам диагностировать это, - это страница .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>