使用 ASP.NET 編寫(xiě) Web 應用程序的簡(jiǎn)單程度令人不敢相信。正因為如此簡(jiǎn)單,所以很多開(kāi)發(fā)人員就不會(huì )花時(shí)間來(lái)設計其應用程序的結構,以獲得更好的性能了。在本文中,我將講述 10 個(gè)用于編寫(xiě)高性能 Web 應用程序的技巧。但是我并不會(huì )將這些建議僅局限于 ASP.NET 應用程序,因為這些應用程序只是 Web 應用程序的一部分。本文不作為對 Web 應用程序進(jìn)行性能調整的權威性指南 — 一整本書(shū)恐怕都無(wú)法輕松講清楚這個(gè)問(wèn)題。請將本文視作一個(gè)很好的起點(diǎn)。
成為工作狂之前,我原來(lái)喜歡攀巖。在進(jìn)行任何大型攀巖活動(dòng)之前,我都會(huì )首先仔細查看指南中的路線(xiàn),閱讀以前游客提出的建議。但是,無(wú)論指南怎么好,您都需要真正的攀巖體驗,然后才能?chē)L試一個(gè)特別具有挑戰性的攀登。與之相似,當您面臨修復性能問(wèn)題或者運行一個(gè)高吞吐量站點(diǎn)的問(wèn)題時(shí),您只能學(xué)習如何編寫(xiě)高性能 Web 應用程序。
我的個(gè)人體驗來(lái)自在 Microsoft 的 ASP.NET 部門(mén)作為基礎架構程序經(jīng)理的經(jīng)驗,在此期間我運行和管理 www.ASP.NET,幫助設計社區服務(wù)器的結構,社區服務(wù)器是幾個(gè)著(zhù)名 ASP.NET 應用程序(組合到一個(gè)平臺的 ASP.NET Forums、.Text 和 nGallery)。我確信有些曾經(jīng)幫助過(guò)我的技巧對您肯定也會(huì )有所幫助。
您應該考慮將應用程序分為幾個(gè)邏輯層。您可能聽(tīng)說(shuō)過(guò) 3 層(或者 n 層)物理體系結構一詞。這些通常都是規定好的體系結構方式,將功能在進(jìn)程和/或硬件之間進(jìn)行了物理分離。當系統需要擴大時(shí),可以很輕松地添加更多的硬件。但是會(huì )出現一個(gè)與進(jìn)程和機器跳躍相關(guān)的性能下降,因此應該避免。所以,如果可能的話(huà),請盡量在同一個(gè)應用程序中一起運行 ASP.NET 頁(yè)及其相關(guān)組件。
因為代碼分離以及層之間的邊界,所以使用 Web 服務(wù)或遠程處理將會(huì )使得性能下降 20% 甚至更多。
數據層有點(diǎn)與眾不同,因為通常情況下,最好具有專(zhuān)用于數據庫的硬件。然而進(jìn)程跳躍到數據庫的成本依然很高,因此數據層的性能是您在優(yōu)化代碼時(shí)首先要考慮的問(wèn)題。
在深入應用程序的性能修復問(wèn)題之前,請首先確保對應用程序進(jìn)行剖析,以便找出具體的問(wèn)題所在。主要性能計數器(如表示執行垃圾回收所需時(shí)間百分比的計數器)對于找出應用程序在哪些位置花費了其主要時(shí)間也非常有用。然而花費時(shí)間的位置通常非常不直觀(guān)。
本文講述了兩種類(lèi)型的性能改善:大型優(yōu)化(如使用 ASP.NET 緩存),和進(jìn)行自身重復的小型優(yōu)化。這些小型優(yōu)化有時(shí)特別有意思。您對代碼進(jìn)行一點(diǎn)小小的更改,就會(huì )獲得很多很多時(shí)間。使用大型優(yōu)化,您可能會(huì )看到整體性能的較大飛躍。而使用小型優(yōu)化時(shí),對于某個(gè)特定請求可能只會(huì )節省幾毫秒的時(shí)間,但是每天所有請求加起來(lái),則可能會(huì )產(chǎn)生巨大的改善。
談到應用程序的性能調整,有一個(gè)試紙性的測試可用來(lái)對工作進(jìn)行優(yōu)先級劃分:代碼是否訪(fǎng)問(wèn)數據庫?如果是,頻率是怎樣的?請注意,這一相同測試也可應用于使用 Web 服務(wù)或遠程處理的代碼,但是本文對這些內容未做講述。
如果某個(gè)特定的代碼路徑中必需進(jìn)行數據庫請求,并且您認為要首先優(yōu)化其他領(lǐng)域(如字符串操作),則請停止,然后執行這個(gè)試紙性測試。如果您的性能問(wèn)題不是非常嚴重的話(huà),最好花一些時(shí)間來(lái)優(yōu)化一下與數據庫、返回的數據量、進(jìn)出數據庫的往返頻率相關(guān)的花費時(shí)間。
了解這些常規信息之后,我們來(lái)看一下可能會(huì )有助于提高應用程序性能的十個(gè)技巧。首先,我要講述可能會(huì )引起最大改觀(guān)的更改。
仔細查看您的數據庫代碼,看是否存在多次進(jìn)入數據庫的請求路徑。每個(gè)這樣的往返都會(huì )降低應用程序可以提供的每秒請求數量。通過(guò)在一個(gè)數據庫請求中返回多個(gè)結果集,可以節省與數據庫進(jìn)行通信所需的總時(shí)間長(cháng)度。同時(shí)因為減少了數據庫服務(wù)器管理請求的工作,還會(huì )使得系統伸縮性更強。
雖然可以使用動(dòng)態(tài) SQL 返回多個(gè)結果集,但是我首選使用存儲過(guò)程。關(guān)于業(yè)務(wù)邏輯是否應該駐留于存儲過(guò)程的問(wèn)題還存在一些爭議,但是我認為,如果存儲過(guò)程中的邏輯可以約束返回數據的話(huà)(縮小數據集的大小、縮短網(wǎng)絡(luò )上所花費時(shí)間,不必篩選邏輯層的數據),則應贊成這樣做。
使用 SqlCommand 實(shí)例及其 ExecuteReader 方法填充強類(lèi)型的業(yè)務(wù)類(lèi)時(shí),可以通過(guò)調用 NextResult 將結果集指針向前移動(dòng)。圖 1 顯示了使用類(lèi)型類(lèi)填充幾個(gè) ArrayList 的示例會(huì )話(huà)。只從數據庫返回您需要的數據將進(jìn)一步減少服務(wù)器上的內存分配。
ASP.NET DataGrid 具有一個(gè)很好的功能:數據分頁(yè)支持。在 DataGrid 中啟用分頁(yè)時(shí),一次會(huì )顯示固定數量的記錄。另外,在 DataGrid 的底部還會(huì )顯示分頁(yè) UI,以便在記錄之間進(jìn)行導航。該分頁(yè) UI 使您能夠在所顯示的數據之間向前和向后導航,并且一次顯示固定數量的記錄。
還有一個(gè)小小的波折。使用 DataGrid 的分頁(yè)需要所有數據均與網(wǎng)格進(jìn)行綁定。例如,您的數據層需要返回所有數據,那么 DataGrid 就會(huì )基于當前頁(yè)篩選顯示的所有記錄。如果通過(guò) DataGrid 進(jìn)行分頁(yè)時(shí)返回了 100,000 個(gè)記錄,那么針對每個(gè)請求會(huì )放棄 99,975 個(gè)記錄(假設每頁(yè)大小為 25 個(gè)記錄)。當記錄的數量不斷增加時(shí),應用程序的性能就會(huì )受到影響,因為針對每個(gè)請求必須發(fā)送越來(lái)越多的數據。
要編寫(xiě)性能更好的分頁(yè)代碼,一個(gè)極佳的方式是使用存儲過(guò)程。圖 2 顯示了針對 Northwind 數據庫中的 Orders 表進(jìn)行分頁(yè)的一個(gè)示例存儲過(guò)程。簡(jiǎn)而言之,您此時(shí)要做的只是傳遞頁(yè)索引和頁(yè)大小。然后就會(huì )計算合適的結果集,并將其返回。
在社區服務(wù)器中,我們編寫(xiě)了一個(gè)分頁(yè)服務(wù)器控件,以完成所有的數據分頁(yè)。您將會(huì )看到,我使用的就是技巧 1 中討論的理念,從一個(gè)存儲過(guò)程返回兩個(gè)結果集:記錄的總數和請求的數據。
返回記錄的總數可能會(huì )根據所執行查詢(xún)的不同而有所變化。例如,WHERE 子句可用來(lái)約束返回的數據。為了計算在分頁(yè) UI 中顯示的總頁(yè)數,必須了解要返回記錄的總數。例如,如果總共有 1,000,000 條記錄,并且要使用一個(gè) WHERE 子句將其篩選為 1000 條記錄,那么分頁(yè)邏輯就需要了解記錄的總數才能正確呈現分頁(yè) UI。
在 Web 應用程序和 SQL Server? 之間設置 TCP 連接可能是一個(gè)非常消耗資源的操作。Microsoft 的開(kāi)發(fā)人員到目前為止能夠使用連接池已經(jīng)有一段時(shí)間了,這使得他們能夠重用數據庫連接。他們不是針對每個(gè)請求都設置一個(gè)新的 TCP 連接,而是只在連接池中沒(méi)有任何連接時(shí)才設置新連接。當連接關(guān)閉時(shí),它會(huì )返回連接池,在其中它會(huì )保持與數據庫的連接,而不是完全破壞該 TCP 連接。
當然,您需要小心是否會(huì )出現泄漏連接。當您完成使用連接時(shí),請一定要關(guān)閉這些連接。再重復一遍:無(wú)論任何人對 Microsoft?.NET Framework 中的垃圾回收有什么評論,請一定要在完成使用連接時(shí)針對該連接顯式調用 Close 或 Dispose。不要相信公共語(yǔ)言運行庫 (CLR) 會(huì )在預先確定的時(shí)間為您清除和關(guān)閉連接。盡管 CLR 最終會(huì )破壞該類(lèi),并強制連接關(guān)閉,但是當針對對象的垃圾回收真正發(fā)生時(shí),并不能保證。
要以最優(yōu)化的方式使用連接池,需要遵守一些規則。首先打開(kāi)連接,執行操作,然后關(guān)閉該連接。如果您必須如此的話(huà),可以針對每個(gè)請求多次打開(kāi)和關(guān)閉連接(最好應用技巧 1),但是不要一直將連接保持打開(kāi)狀態(tài)并使用各種不同的方法對其進(jìn)行進(jìn)出傳遞。第二,使用相同的連接字符串(如果使用集成身份驗證的話(huà),還要使用相同的線(xiàn)程標識)。如果不使用相同的連接字符串,例如根據登錄的用戶(hù)自定義連接字符串,那么您將無(wú)法得到連接池提供的同一個(gè)優(yōu)化值。如果您使用集成身份驗證,同時(shí)還要模擬大量用戶(hù),連接池的效率也會(huì )大大下降。嘗試跟蹤與連接池相關(guān)的任何性能問(wèn)題時(shí),.NET CLR 數據性能計數器可能非常有用。
每當應用程序連接資源時(shí),如在另一個(gè)進(jìn)程中運行的數據庫,您都應該重點(diǎn)考慮連接該資源所花時(shí)間、發(fā)送或檢索數據所花時(shí)間,以及往返的數量,從而進(jìn)行優(yōu)化。優(yōu)化應用程序中任何種類(lèi)的進(jìn)程跳躍都是獲得更佳性能的首要一點(diǎn)。
應用層包含了連接數據層、將數據轉換為有意義類(lèi)實(shí)例和業(yè)務(wù)流程的邏輯。例如社區服務(wù)器,您要在其中填充Forums 或 Threads集合,應用業(yè)務(wù)規則(如權限);最重要的是要在其中執行緩存邏輯。
編寫(xiě)應用程序代碼行之前,一個(gè)首要完成的操作是設計應用層的結構,以便最大化利用 ASP.NET 緩存功能。
如果您的組件要在 ASP.NET 應用程序中運行,則只需在該應用程序項目中包括一個(gè) System.Web.dll 引用。當您需要訪(fǎng)問(wèn)該緩存時(shí),請使用 HttpRuntime.Cache 屬性(通過(guò) Page.Cache 和 HttpContext.Cache 也可訪(fǎng)問(wèn)這個(gè)對象)。
對于緩存數據,有幾個(gè)規則。首先,如果數據可能會(huì )多次使用時(shí),則這是使用緩存的一個(gè)很好的備選情況。第二,如果數據是通用的,而不特定于某個(gè)具體的請求或用戶(hù)時(shí),則也是使用緩存的一個(gè)很好的備選情況。如果數據是特定于用戶(hù)或請求的,但是壽命較長(cháng)的話(huà),仍然可以對其進(jìn)行緩存,但是這種情況可能并不經(jīng)常使用。第三,一個(gè)經(jīng)常被忽略的規則是,有時(shí)可能您緩存得太多。通常在一個(gè) x86 計算機上,為了減少內存不足錯誤出現的機會(huì ),您會(huì )想使用不高于 800MB 的專(zhuān)用字節運行進(jìn)程。因此緩存應該有個(gè)限度。換句話(huà)說(shuō),您可能能夠重用某個(gè)計算結果,但是如果該計算采用 10 個(gè)參數的話(huà),您可能要嘗試緩存 10 個(gè)排列,這樣有可能給您帶來(lái)麻煩。一個(gè)要求 ASP.NET 的最常見(jiàn)支持是由于過(guò)度緩存引起的內存不足錯誤,尤其是對于大型數據集。

緩存有幾個(gè)極佳的功能,您需要對它們有所了解。首先,緩存會(huì )實(shí)現最近最少使用的算法,使得 ASP.NET 能夠在內存運行效率較低的情況下強制緩存清除 - 從緩存自動(dòng)刪除未使用過(guò)的項目。第二,緩存支持可以強制失效的過(guò)期依賴(lài)項。這些依賴(lài)項包括時(shí)間、密鑰和文件。時(shí)間經(jīng)常會(huì )用到,但是對于 ASP.NET 2.0,引入了一個(gè)功能更強的新失效類(lèi)型:數據庫緩存失效。它指的是當數據庫中的數據發(fā)生變化時(shí)自動(dòng)刪除緩存中的項。有關(guān)數據庫緩存失效的詳細信息,請參閱 MSDN?Magazine 2004 年 7 月的 Dino Esposito Cutting Edge 專(zhuān)欄。要了解緩存的體系結構,請參閱圖 3。
在本文前面部分,我提到了經(jīng)常遍歷代碼路徑的一些小改善可能會(huì )導致較大的整體性能收益。對于這些小改善,其中有一個(gè)絕對是我的最?lèi)?ài),我將其稱(chēng)之為“每請求緩存”。
緩存 API 的設計目的是為了將數據緩存較長(cháng)的一段時(shí)間,或者緩存至滿(mǎn)足某些條件時(shí),但每請求緩存則意味著(zhù)只將數據緩存為該請求的持續時(shí)間。對于每個(gè)請求,要經(jīng)常訪(fǎng)問(wèn)某個(gè)特定的代碼路徑,但是數據卻只需提取、應用、修改或更新一次。這聽(tīng)起來(lái)有些理論化,那么我們來(lái)舉一個(gè)具體的示例。
在社區服務(wù)器的論壇應用程序中,頁(yè)面上使用的每個(gè)服務(wù)器控件都需要個(gè)性化的數據來(lái)確定使用什么外觀(guān)、使用什么樣式表,以及其他個(gè)性化數據。這些數據中有些可以長(cháng)期緩存,但是有些數據卻只針對每個(gè)請求提取一次,然后在執行該請求期間對其重用多次,如要用于控件的外觀(guān)。
為了達到每請求緩存,請使用 ASP.NET HttpContext。對于每個(gè)請求,都會(huì )創(chuàng )建一個(gè) HttpContext 實(shí)例,在該請求期間從 HttpContext.Current 屬性的任何位置都可訪(fǎng)問(wèn)該實(shí)例。該 HttpContext 類(lèi)具有一個(gè)特殊的 Items 集合屬性;添加到此 Items 集合的對象和數據只在該請求持續期間內進(jìn)行緩存。正如您可以使用緩存來(lái)存儲經(jīng)常訪(fǎng)問(wèn)的數據一樣,您也可以使用 HttpContext.Items 來(lái)存儲只基于每個(gè)請求使用的數據。它背后的邏輯非常簡(jiǎn)單:數據在它不存在的時(shí)候添加到 HttpContext.Items 集合,在后來(lái)的查找中,只是返回 HttpContext.Items 中的數據。
技巧 6 — 后臺處理
通往代碼的路徑應該盡可能快速,是嗎?可能有時(shí)您會(huì )覺(jué)得針對每個(gè)請求執行的或者每 n 個(gè)請求執行一次的任務(wù)所需資源非常多。發(fā)送電子郵件或者分析和驗證傳入數據就是這樣的一些例子。
剖析 ASP.NET Forums 1.0 并重新構建組成社區服務(wù)器的內容時(shí),我們發(fā)現添加新張貼的代碼路徑非常慢。每次添加新張貼時(shí),應用程序首先需要確保沒(méi)有重復的張貼,然后必須使用“壞詞”篩選器分析該張貼,分析張貼的字符圖釋?zhuān)瑢堎N添加標記并進(jìn)行索引,請求時(shí)將張貼添加到合適的隊列,驗證附件,最終張貼之后,立即向所有訂閱者發(fā)出電子郵件通知。很清楚,這涉及很多操作。
經(jīng)研究發(fā)現,大多數時(shí)間都花在了索引邏輯和發(fā)送電子郵件上。對張貼進(jìn)行索引是一個(gè)非常耗時(shí)的操作,人們發(fā)現內置的 System.Web.Mail 功能要連接 SMYP 服務(wù)器,然后連續發(fā)送電子郵件。當某個(gè)特定張貼或主題領(lǐng)域的訂閱者數量增加時(shí),執行 AddPost 功能所需的時(shí)間也越來(lái)越長(cháng)。
并不需要針對每個(gè)請求都進(jìn)行電子郵件索引。理想情況下,我們想要將此操作進(jìn)行批處理,一次索引 25 個(gè)張貼或者每五分鐘發(fā)送一次所有電子郵件。我們決定使用以前用于對數據緩存失效進(jìn)行原型設計的代碼,這個(gè)失效是用于最終進(jìn)入 Visual Studio? 2005 的內容的。
System.Threading 命名空間中的 Timer 類(lèi)非常有用,但是在 .NET Framework 中不是很有名,至少對于 Web 開(kāi)發(fā)人員來(lái)說(shuō)是這樣。創(chuàng )建之后,這個(gè) Timer 類(lèi)將以一個(gè)可配置的間隔針對 ThreadPool 中的某個(gè)線(xiàn)程調用指定的回調。這就表示,您可以對代碼進(jìn)行設置,使其能夠在沒(méi)有對 ASP.NET 應用程序進(jìn)行傳入請求的情況下得以執行,這是后臺處理的理想情況。您還可以在此后臺進(jìn)程中執行如索引或發(fā)送電子郵件之類(lèi)的操作。
但是,這一技術(shù)有幾個(gè)問(wèn)題。如果應用程序域卸載,該計時(shí)器實(shí)例將停止觸發(fā)其事件。另外,因為 CLR 對于每個(gè)進(jìn)程的線(xiàn)程數量具有一個(gè)硬性標準,所以可能會(huì )出現這樣的情形:服務(wù)器負載很重,其中計時(shí)器可能沒(méi)有可在其基礎上得以完成的線(xiàn)程,在某種程度上可能會(huì )造成延遲。ASP.NET 通過(guò)在進(jìn)程中保留一定數量的可用線(xiàn)程,并且僅使用總線(xiàn)程的一部分用于請求處理,試圖將上述情況發(fā)生的機會(huì )降到最低。但是,如果您具有很多異步操作時(shí),這可能就是一個(gè)問(wèn)題了。
這里沒(méi)有足夠的空間來(lái)放置該代碼,但是您可以下載一個(gè)可以看懂的示例,網(wǎng)址是 www.rob-howard.net。請了解一下 Blackbelt TechEd 2004 演示中的幻燈片和演示。
ASP.NET 是您的表示層(或者說(shuō)應該是您的表示層);它由頁(yè)、用戶(hù)控件、服務(wù)器控件(HttpHandlers 和 HttpModules)以及它們生成的內容組成。如果您具有一個(gè) ASP.NET 頁(yè),它會(huì )生成輸出(HTML、XML、圖像或任何其他數據),并且您針對每個(gè)請求運行此代碼時(shí),它都會(huì )生成相同的輸出,那么您就擁有一個(gè)可用于頁(yè)輸出緩存的絕佳備選內容。
將此行內容添加頁(yè)的最上端
<%@ Page OutputCache VaryByParams="none" Duration="60" %>
就可以高效地為此頁(yè)生成一次輸出,然后對它進(jìn)行多次重用,時(shí)間最長(cháng)為 60 秒,此時(shí)該頁(yè)將重新執行,輸出也將再一次添加到 ASP.NET 緩存。通過(guò)使用一些低級程序化 API 也可以完成此行為。對于輸出緩存有幾個(gè)可配置的設置,如剛剛講到的 VaryByParams 屬性。VaryByParams 剛好被請求到,但還允許您指定 HTTP GET 或 HTTP POST 參數來(lái)更改緩存項。例如,只需設置 VaryByParam="Report" 即可對 default.aspx?Report=1 或 default.aspx?Report=2 進(jìn)行輸出緩存。通過(guò)指定一個(gè)以分號分隔的列表,還可以指定其他參數。
很多人都不知道何時(shí)使用輸出緩存,ASP.NET 頁(yè)還會(huì )生成一些位于緩存服務(wù)器下游的 HTTP 標頭,如 Microsoft Internet Security and Acceleration Server 或 Akamai 使用的標頭。設置了 HTTP 緩存標頭之后,可以在這些網(wǎng)絡(luò )資源上對文檔進(jìn)行緩存,客戶(hù)端請求也可在不必返回原始服務(wù)器的情況下得以滿(mǎn)足。
因此,使用頁(yè)輸出緩存不會(huì )使得您的應用程序效率更高,但是它可能會(huì )減少服務(wù)器上的負載,因為下游緩存技術(shù)會(huì )緩存文檔。當然,這可能只是匿名內容;一旦它成為下游之后,您就再也不會(huì )看到這些請求,并且再也無(wú)法執行身份驗證以阻止對它的訪(fǎng)問(wèn)了。
如果您未運行 IIS 6.0 (Windows Server? 2003),那么您就錯過(guò)了 Microsoft Web 服務(wù)器中的一些很好的性能增強。在技巧 7 中,我討論了輸出緩存。在 IIS 5.0 中,請求是通過(guò) IIS 然后進(jìn)入 ASP.NET 的。涉及到緩存時(shí),ASP.NET 中的 HttpModule 會(huì )接收該請求,并返回緩存中的內容。
如果您正在使用 IIS 6.0,就會(huì )發(fā)現一個(gè)很好的小功能,稱(chēng)為內核緩存,它不需要對 ASP.NET 進(jìn)行任何代碼更改。當請求由 ASP.NET 進(jìn)行輸出緩存時(shí),IIS 內核緩存會(huì )接收緩存數據的一個(gè)副本。當請求來(lái)自網(wǎng)絡(luò )驅動(dòng)程序時(shí),內核級別的驅動(dòng)程序(無(wú)上下文切換到用戶(hù)模式)就會(huì )接收該請求,如果經(jīng)過(guò)了緩存,則會(huì )將緩存的數據刷新到響應,然后完成執行。這就表示,當您將內核模式緩存與 IIS 和 ASP.NET 輸出緩存一起使用時(shí),就會(huì )看到令人不敢相信的性能結果。在 ASP.NET 的 Visual Studio 2005 開(kāi)發(fā)過(guò)程中,我一度是負責 ASP.NET 性能的程序經(jīng)理。開(kāi)發(fā)人員完成具體工作,但是我要看到每天進(jìn)行的所有報告。內核模式緩存結果總是最有意思的。最常見(jiàn)的特征是網(wǎng)絡(luò )充滿(mǎn)了請求/響應,而 IIS 運行時(shí)的 CPU 使用率只有大約 5%。這太令人震驚了!當然使用 IIS 6.0 還有一些其他原因,但是內核模式緩存是其中最明顯的一個(gè)。
雖然使用 gzip 并不一定是服務(wù)器性能技巧(因為您可能會(huì )看到 CPU 使用率的提高),但是使用 gzip 壓縮可以減少服務(wù)器發(fā)送的字節數量。這就使人們覺(jué)得頁(yè)速度加快了,并且還減少了帶寬的用量。根據所發(fā)送數據、可以壓縮的程度以及客戶(hù)端瀏覽器是否支持(IIS 只會(huì )向支持 gzip 壓縮的客戶(hù)端發(fā)送經(jīng)過(guò) gzip 壓縮的內容,如 Internet Explorer 6.0 和 Firefox),您的服務(wù)器每秒可以服務(wù)于更多的請求。實(shí)際上,幾乎每當您減少所返回數據的數量時(shí),都會(huì )增加每秒請求數。
Gzip 壓縮已經(jīng)內置到 IIS 6.0 中,并且其性能比 IIS 5.0 中使用的 gzip 壓縮要好的多,這是好消息。但不幸的是,當嘗試在 IIS 6.0 中打開(kāi) gzip 壓縮時(shí),您可能無(wú)法在 IIS 的屬性對話(huà)中找到該設置。IIS 小組在該服務(wù)器中置入了卓越的 gzip 功能,但是忘了包括一個(gè)用于啟用該功能的管理 UI。要啟用 gzip 壓縮,您必須深入到 IIS 6.0 的 XML 配置設置內部(這樣不會(huì )引起心臟虛弱)。順便提一句,這歸功于 OrcsWeb 的 Scott Forsyth,他幫助我提出了在 OrcsWeb 上宿主的 www.asp.net 服務(wù)器的這個(gè)問(wèn)題。
本文就不講述步驟了,請閱讀 Brad Wilson 的文章,網(wǎng)址是 IIS6 Compression。還有一篇有關(guān)為 ASPX 啟用壓縮的知識庫文章,網(wǎng)址是 Enable ASPX Compression in IIS。但是您應該注意,由于一些實(shí)施細節,IIS 6.0 中不能同時(shí)存在動(dòng)態(tài)壓縮和內核緩存。
視圖狀態(tài)是一個(gè)有趣的名稱(chēng),用于表示在所生成頁(yè)的隱藏輸出字段中存儲一些狀態(tài)數據的 ASP.NET。當該頁(yè)張貼回服務(wù)器時(shí),服務(wù)器可以分析、驗證、并將此視圖狀態(tài)數據應用回該頁(yè)的控件樹(shù)。視圖狀態(tài)是一個(gè)非常強大的功能,因為它允許狀態(tài)與客戶(hù)端一起保持,并且它不需要 cookie 或服務(wù)器內存即可保存此狀態(tài)。很多 ASP.NET 服務(wù)器控件都使用視圖狀態(tài)來(lái)保持在與頁(yè)元素進(jìn)行交互期間創(chuàng )建的設置,例如保存對數據進(jìn)行分頁(yè)時(shí)顯示的當前頁(yè)。
然而使用視圖狀態(tài)也有一些缺點(diǎn)。首先,服務(wù)或請求頁(yè)時(shí),它都會(huì )增加頁(yè)的總負載。對張貼回服務(wù)器的視圖狀態(tài)數據進(jìn)行序列化或取消序列化時(shí),也會(huì )發(fā)生額外的開(kāi)銷(xiāo)。最后,視圖狀態(tài)會(huì )增加服務(wù)器上的內存分配。
幾個(gè)服務(wù)器控件有著(zhù)過(guò)度使用視圖狀態(tài)的趨勢,即使在并不需要的情況下也要使用它,其中最著(zhù)名的是 DataGrid。ViewState 屬性的默認行為是啟用,但是如果您不需要,則可以在控件或頁(yè)級別關(guān)閉。在控件內,只需將 EnableViewState 屬性設置為 false,或者在頁(yè)中使用下列設置即可對其進(jìn)行全局設置:
<%@ Page EnableViewState="false" %>
如果您不回發(fā)頁(yè),或者總是針對每個(gè)請求重新生成頁(yè)上的控件,則應該在頁(yè)級別禁用視圖狀態(tài)。
我為您講述了一些我認為在編寫(xiě)高性能 ASP.NET 應用程序時(shí)有所幫助的技巧。正如我在本文前面部分提到的那樣,這是一個(gè)初步指南,并不是 ASP.NET 性能的最后結果。(有關(guān)改善 ASP.NET 應用程序性能的信息,請參閱 Improving ASP.NET Performance。)只有通過(guò)自己的親身體驗才能找出解決具體性能問(wèn)題的最好方法。但是,在您的旅程中,這些技巧應該會(huì )為您提供一些好的指南。在軟件開(kāi)發(fā)中,幾乎沒(méi)有絕對的東西;每個(gè)應用程序都是唯一的。
聯(lián)系客服