12

これに対する答えは既に知っていますが、Microsoft から文書化されていないため、コミュニティと共有したいと考えていました。

シナリオ: トラフィックの急増が IIS 7.5 ASP.NET Web サイトを襲い、要求が待ち行列に入り始めていることに気付きました。サイトのパフォーマンスは非常に遅くなりますが、CPU と RAM は十分に利用できます。

これは、内部 Web サービス呼び出しを大量に行ったサイトで最近見られた問題です。内部ヘルス チェックがタイムアウトを開始し、このサーバーがクラスターから脱落する原因となります。(ただし、このサーバーは最も強力なハードウェアです...)

4

1 に答える 1

13

インターネットで検索したところ、この問題に関連する Microsoft の次の記事を見つけました。

KB 821268: ASP.NET アプリケーションから Web サービス要求を行うと、競合、パフォーマンスの低下、およびデッドロックが発生する

この記事では、パフォーマンスを微調整するための優れたヒントをいくつか紹介していますが、遭遇したいくつかの非常に重要な上限については触れていません。

私たちにとっての解決策は、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 はこれらの値を計算するための方程式をいくつか提案しています。ただし、これらの数値の最大値が INT のサイズ、つまり 32767 であることは言及されていません。

したがって、これらを理解するための正しい方程式は次のとおりです。

  • maxWorkerThreads : 32767 / #コア
    • 私たちの場合、24 コアのサーバーがあります。そのため、maxWorkerThreads 値は正しく 1365 に設定されています。32767 より大きい整数になる数値は、サーバーによって maxWorkerThreads が 32767 に設定されます。
  • maxIoThreads : maxWorkerThreads と同じ (32767 / #Cores)
  • minWorkerThreads : maxWorkerThreads / 2
    • これはトリッキーでした。1 つが 32767 より大きい整数値を超えた場合 (KB 記事に記載されていることにもかかわらず、この数値は使用しているコアの数で乗算されます)、「最大」値とは異なり、これはデフォルトでマシンのコアの数になります! 私たちの場合、これは 24 に設定されており (最小値に任意に高い値を設定したため)、サーバーのパフォーマンスが低下していました。
  • minIoThreads : minWorkerThreads と同じ
  • minFreeThreads : 88 * #Cores (KB 記事から直接取得)
  • minLocalRequestFreeThreads : 76 * #Cores (KB 記事から直接取得)

このソリューションは万人向けではなく、KB 記事の基準を満たす場合にのみ使用してください。

これを診断するために使用したもう 1 つのツールは、コード ビハインドのない .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>
于 2012-05-07T15:31:16.747 に答える