Server 2008 RC2、IIS 7.5、ASP.NET 和排隊請求性能不佳
我已經知道這個問題的答案,但想與社區分享,因為它沒有從 Microsoft 記錄。
場景:流量激增訪問您的 IIS 7.5 ASP.NET 網站,您注意到請求開始排隊。該網站的性能緩慢到爬行,但您有足夠的 CPU 和 RAM 可用。
這是我們最近在一個網站上看到的問題,該網站進行了一系列內部 Web 服務呼叫。內部健康檢查將開始超時,這將導致該伺服器退出我們的集群。(然而,這台伺服器是最強大的硬體……)
在網際網路上搜尋後,我發現了以下來自 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”,因為它們取決於您的硬體。
從上面的知識庫文章中,微軟提出了一些計算這些值的方程。但是,他們沒有提到這些數字的最大值是 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>