常見的 ASP.NET 性能神話
有用的 ASP.NET 性能技巧和訣竅
在 ASP.NET 中處理數(shù)據(jù)庫的一些建議
緩沖以及用 ASP.NET 進行后臺處理
本文使用下列技術:ASP.NET,.NET 框架,IIS
用 ASP.NET 編寫 Web 應用程序其輕松程度令人難以置信。它是如此的容易,以至于許多開發(fā)人員不用花費多少時間來構筑其應用便能獲得非常好的性能。在本文中,我將給出10個編寫高性能 Web 應用的技巧。我的評論不僅僅局限與 ASP.NET 應用,因為它們只是 Web 應用的一個子集。本文也不是 Web 應用性能調整的權威指南——這方面的內(nèi)容可以寫成一本書。相反,本文可以被視作一個好的起點。
在廢寢忘食地工作之前,我常常要去攀巖。在攀巖之前,我總是要看一下指南手冊中的線路并閱讀以前來此一游的人留下的建議和忠告。但是,不管指南手冊有多磨好,在嘗試一次特定的具有挑戰(zhàn)性的攀爬之前,你都必須付諸實際的行動。同樣,在你面臨解決的性能問題或者營運一個高吞吐量的站點之前,你只能想方設法編寫高性能 Web 應用程序。
我們個人經(jīng)驗來自在微軟 ASP.NET 團隊從事底層架構程序經(jīng)理,運行和管理 www.asp.net ,并協(xié)助架構 Community Server 過程中的經(jīng)歷,Community Server 是幾個有名的 ASP.NET 應用程序的下一個版本(它將 ASP.NET Forums,.Text 和 nGallery 整合到一個平臺)。我確信這些幫助過我的技巧也會對你有所裨益。
你應該考慮將應用程序分離成幾個邏輯層。你可能聽說過術語3-層(或n-層)物理體系結構。它們通常是跨進程和/或硬件對功能進行物理劃分的規(guī)定的體系結構模式。當系統(tǒng)需要伸縮時,更多的硬件能被添加。然而,總是應該避免與進程和機器忙碌程度相關的性能問題。所以,不管什么時候,只要可能,都要在相同的應用中一起運行 ASP.NET 頁面及其相關的組件。
由于代碼和層之間的邊界分離,使用 Web 服務或遠程調用將降低20%以上的性能。
數(shù)據(jù)層則稍微有些不同,因為數(shù)據(jù)庫通常都用專門的硬件。但是,數(shù)據(jù)庫的處理成本仍然很高,因此最優(yōu)化代碼時,數(shù)據(jù)層的性能應該是首當其充要關注的地方。
在著手解決你的應用程序的性能問題之前,一定要剖析應用程序,確定問題之所在。獲取關鍵的性能計數(shù)器值(如實現(xiàn)垃圾收集所花時間之百分比的性能計數(shù)器的值)對于查找應用程序在何處最耗時也是非常重要的。憑借直覺常常也能找到耗時所在。
本文所描述的性能改進有兩種類型:大型優(yōu)化,如使用 ASP.NET Cache,以及不斷重復進行的微型優(yōu)化。這些微型優(yōu)化有時很有意思。對代碼的小小改動便會引起很大的動靜,產(chǎn)生成千次的調用。對于大型優(yōu)化,你可能會看到整體性能的大跳躍。而對微型優(yōu)化,給定請求可能只是毫秒級的調整,但按每天的請求總數(shù)計算,其結果的改進可能是巨大的。
數(shù)據(jù)層的性能
當調整某個應用程序的性能時,有一個簡單的試金石,你可以用它按先后次序:檢查代碼是否存取數(shù)據(jù)庫?如果是,多長時間存取一次?注意相同的測試也可以被應用于使用 Web 服務或遠程調用的代碼,但我們本文中不涉及這方面內(nèi)容。
如果在特定的代碼流程中必須具有對數(shù)據(jù)庫的請求以及要考察其它方面,如:想對字符串處理進行優(yōu)先優(yōu)化,那么暫且把它放一放,先按照上面定好的優(yōu)先次序來做。除非你有異乎尋常的性能問題,否則你的時間應該用在嘗試最優(yōu)化與數(shù)據(jù)庫的連接所花的時間,返回的數(shù)據(jù)量以及多長時間往返一次和數(shù)據(jù)庫的通訊上。
有了這些概括信息,下面就讓我們來看看能幫助你改善應用程序性能的十個技巧。我將從能獲得最顯著效果的改變開始。
技巧 1 —— 返回多個結果集
復審你的數(shù)據(jù)庫代碼,看看是否有多于一次的對數(shù)據(jù)庫的訪問請求。這樣每次往返數(shù)據(jù)庫都降低你的應用程序能處理的每秒請求數(shù)。通過在單個數(shù)據(jù)庫請求中返回多結果集,你能降低與數(shù)據(jù)庫通信的總體時間。同時你也將使系統(tǒng)更具伸縮性,因為你減少了數(shù)據(jù)庫服務器處理請求的負擔。
雖然你可以用動態(tài) SQL 返回多結果集,我更喜歡使用存儲過過程。是否將業(yè)務邏輯駐留在存儲過程當中是個有待爭論的問題,但我認為,如果存儲過程中的邏輯能約束返回的數(shù)據(jù)(降低數(shù)據(jù)集的尺寸,在網(wǎng)絡上傳輸?shù)臅r間以及邏輯層不必過慮數(shù)據(jù)),這是一件好事情。
使用 SqlCommand 命令實例及其 ExecuteReader 方法來處理強類型的各個業(yè)務類,你通過調用 NextResult 可以向前移動結果集指針。Figure 1 示范了處理幾個帶類型的 ArrayLists 例子會話。從數(shù)據(jù)庫只返回你需要的數(shù)據(jù)還會降低服務器上內(nèi)存的分配。
技巧 2 —— 分頁數(shù)據(jù)存取
ASP.NET DataGrid 提供了非常好的能力:數(shù)據(jù)分頁支持。當啟用 DataGrid 中的分頁功能,則每次只顯示固定數(shù)量的記錄。此外,分頁用戶界面也會顯示在 DataGrid 底部用于導航記錄。分頁用戶界面允許你向前向后導航所顯示的記錄,一次顯示固定數(shù)量的記錄。
有一個美中不足的是用 DataGrid 分頁需要將所有數(shù)據(jù)邦定到此柵格控件(gird)。例如,你的數(shù)據(jù)層必須返回所有數(shù)據(jù),然后 DataGrid 將根據(jù)當前頁過濾掉所有顯示的記錄。當你通過 DataGrid 進行分頁時,如果有 100,000 條記錄被返回,那么每個請求有 99,975 條記錄將被廢棄掉(假設頁尺寸為 25)。當記錄數(shù)不斷增加,此應用程序的性能便會遭受痛苦,因為每次請求所要發(fā)送的數(shù)據(jù)會越來越多。
編寫較好的分頁代碼的一個好的方法是用存儲過程。Figure 2 示范了一個用 Northwind 數(shù)據(jù)庫中 Orders 表通過存儲過程分頁的例子。很簡單,只要你在頁面中傳遞索引以及頁尺寸即可。相應的結果集先被計算然后被返回。
在 Community Server 中,我們編寫了幾個分頁控件來完成數(shù)據(jù)分頁。你將會看到,我使用了技巧 1 中討論的思路,從一個存儲過程中返回連個結果集:總記錄數(shù)和請求的數(shù)據(jù)。
返回的總記錄數(shù)依賴于所執(zhí)行的查詢不同而不同。例如,某個 WHERE 子句可被用于約束返回的數(shù)據(jù)。為了計算在分頁用戶界面顯示的總頁數(shù),返回的總記錄數(shù)必須是已知的。例如,如果有 1,000,000 條記錄,用一個 WHERE 子句對之過濾后為 1,000 條記錄,則分頁邏輯必須要知道總記錄數(shù)以便在分頁用戶界面中正確呈現(xiàn)。
技巧 3 —— 連接池
建立 Web 應用程序與 SQL Server 之間的 TCP 連接是一項昂貴的操作。微軟的開發(fā)人員利用連接池技術已經(jīng)有好長一段時間了,這個技術使他們能重用到數(shù)據(jù)庫的連接。而不是每次請求都建立新的 TCP 連接,新連接僅在連接池中得不到連接時才建立。當連接被關閉時,它被返回到連接池中,在那里它仍然保持與數(shù)據(jù)庫的連接,與完全斷開 TCP 連接相反。
當然,你需要提防泄漏的連接。當你處理完畢,一定要關閉連接。重申一次:不管人們怎么吹噓微軟 .NET 框架中的垃圾收集特性,每當你處理完畢,一定要顯式地調用連接對象的 Close 或 Dispose 方法。不要指望公共語言運行時(CLR)來為你定時清除和關閉連接。CLR 最終將銷毀類并強行關閉連接,但你無法保證該對象的垃圾收集屆時會起作用。
為了充分用好連接池,有幾條規(guī)則必須了然于心。首先,打開連接,進行處理,然后關閉連接。寧愿每個請求的連接打開和關閉多次,也不要保持連接打開狀態(tài)以及在不同的方法間將它傳來傳去。其次,使用相同的連接串(如果你使用集成身份檢查,那么也要用相同的線程身份)。如果你不用相同的連接串,例如,根據(jù)登錄用戶來定制連接串,你將無法得到連接池所提供的相同的最優(yōu)化值。當模擬大用戶量情形時,如果你使用集成身份檢查,那么你的連接池將效力大減。.NET CLR 數(shù)據(jù)性能計數(shù)器在試圖跟蹤任何與連接池有關的性能問題時是非常有用的。
不管什么時候,只要你的應用程序連接到運行在其它進程中的資源,比如某個數(shù)據(jù)庫,你都應該針對連接到資源所耗時間,發(fā)送和接收數(shù)據(jù)所耗時間以及往返次數(shù)進行優(yōu)化。為了實現(xiàn)較好的性能,應該首當其充優(yōu)化應用程序中任何種類的忙碌進程。
應用層包含到數(shù)據(jù)層的連接以及將數(shù)據(jù)轉換成有意義的類實例和業(yè)務處理的邏輯。以 Community Server 為例,你要在其中處理 Forums 和 Threads 集合;以及應用許可這樣的業(yè)務規(guī)則;尤其重要的是緩沖(Caching)邏輯也實現(xiàn)其中。
技巧 4 —— ASP.NET Cache API
在編寫代碼之前要做的頭等大事之一是最大限度地構建應用層并發(fā)掘 ASP.NET 的 Cache 特性。
如果你的組件在 ASP.NET 應用程序內(nèi)運行,那么你只需要在應用程序工程中引用 System.Web.dll 即可。當你需要訪問 Cache 時,用 HttpRuntime.Cache 屬性(相同的對象也可以通過 Page.Cache 和 HttpContext.Cache 訪問)。
緩沖數(shù)據(jù)有幾個準則。首先,如果數(shù)據(jù)能被使用多次,緩沖是個好的后選方案。其次,如果數(shù)據(jù)對給定請求或用戶是一般的數(shù)據(jù)而非專用數(shù)據(jù),那么最好是選擇緩沖。如果數(shù)據(jù)用戶或請求專用,如果需要保存期很長但可能不被經(jīng)常使用,那么仍然要用緩沖。第三,常常被忽略的一個準則是有時緩沖太多的東西。一般來說,在x86機器上,為了降低內(nèi)存不足錯誤的幾率,運行某個進程不要超過800MB私有字節(jié)。因此,緩沖應該有個上限。換句話說,你也許能重用某個計算的結果,但如果該計算有10個參數(shù),你可能試圖針對10個置換進行緩沖,這樣做可能會給你帶來麻煩。ASP.NET 提供的最常見的容錯是由覆蓋緩沖導致的內(nèi)存不足錯誤,尤其是大型數(shù)據(jù)集。
Cache 有幾個重要特性是必須要了解的。第一個是 Cache 實現(xiàn)了最近最少使用(least-recently-used)算法,允許 ASP.NET 強制 Cache 清除操作 —— 如果可用內(nèi)存下降到低水平 —— 則自動從 Cache 中刪除不使用的項目。第二個是 Cache 支持依賴性到期特性,它能強制包括時間,鍵值,文件失效。時間常常被使用,但 ASP.NET 2.0 引入了具有更強大的失效類型:數(shù)據(jù)庫緩沖失效。也就是當數(shù)據(jù)庫中的數(shù)據(jù)改變時,緩沖中的條目會自動刪除。有關數(shù)據(jù)庫緩沖失效的更多信息參見 Dino Esposito 在 MSDN 雜志 2004 年七月刊的 Cutting Edge 專欄文章。該緩沖的體系結構,參見 Figure 3。
Figure 3 ASP.NET Cache
技巧 5 —— 預請求緩沖(Per-Request Caching)
在本文前面,我曾提到對頻繁執(zhí)行的代碼塊所做的小小改動可能產(chǎn)生很大的,整體性能的提升。我把其中一個我特別中意的叫做預請求緩沖(per-request caching)。
由于 Cache API 被設計用來緩沖長期數(shù)據(jù)或直到某個條件被滿足,預請求緩沖意旨用于請求期間的緩沖該數(shù)據(jù)。特定的代碼流程被每次請求頻繁訪問但是數(shù)據(jù)只需要被拾取,應用,修改或更新一次,這樣說太理論化,還是讓我們看一個具體的例子吧。
在 Community Server 的 Forums (論壇)應用中,某個頁面上使用的每個服務器控件需要個性化數(shù)據(jù)以確定使用那個皮膚和式樣頁,以及其它的個性化數(shù)據(jù),其中有些數(shù)據(jù)可以被長時間緩沖,但有些數(shù)據(jù),比如用于控件的皮膚在單個請求中只被拾取一次并在該請求執(zhí)行期間被重用多次。
為了完成預請求緩沖,用 ASP.NET HttpContext。HttpContext 的實例是隨每個請求創(chuàng)建的,并可以通過 HttpContext.Current 屬性在那個請求執(zhí)行期間的任何地方存取它。HttpContext 類具有一個特別的 Items 集合屬性,被添加到該 Items 集合的對象和數(shù)據(jù)只是在該請求期間被緩存。就像你可以使用 Cache 來保存頻繁使用的數(shù)據(jù)一樣,你可以用 HttpContext.Items 來保存只在某個預請求中使用的數(shù)據(jù)。在此背景后的邏輯很簡單:當數(shù)據(jù)不存在時被添加到 HttpContext.Items 集合,以及在隨后的并發(fā)查找中簡單地返回 HttpContext.Items 中發(fā)現(xiàn)的數(shù)據(jù)。
技巧 6——后臺處理
你的代碼流程應該盡可能快,對吧?你自己可能多次發(fā)現(xiàn)要完成每個請求或每n個請求的任務代價很高。發(fā)出 e-mail 或解析并檢查輸入數(shù)據(jù)的有效性就是個例。
在重新生成 ASP.NET Forums 1.0 并把它整合到 Community Server 時,我們發(fā)現(xiàn)添加新貼的代碼流程非常慢。每次添加帖子,應用程序首先要確保沒有重復貼,然后必須用“badword”過濾器解析該貼的表情圖像,記號并索引,如果必要還要將帖子添加到相應的隊列中,對附件進行有效性檢查,最終完成發(fā)貼后,給預訂者發(fā)出 e-mail 通知。顯然,這里做的工作太多。
我們發(fā)現(xiàn)大多數(shù)時間都花在了索引邏輯和發(fā)送e-mail上。索引帖子是一個很耗時的操作,此外,內(nèi)建的 System.Web.Mail 功能要與 SMTP 服務器連接并順序發(fā)送郵件。當特定帖子或主題預定者數(shù)量增加時,AddPost 函數(shù)的執(zhí)行時間會越來越長。
并不是每個請求都需要索引郵件,我們想最好是批量集中處理,并且一次只索引25個帖子或每隔五分鐘發(fā)送一次郵件。我們決定使用的代碼與我曾在原型數(shù)據(jù)庫緩沖失效中所使用的代碼相同,最終它也被納入 Visual Studio 2005。
名字空間 System.Threading 中的 Timer 類非常有用,但在.NET 框架中鮮為人知,至少對 Web 開發(fā)者來說是這樣。一旦創(chuàng)建,Timer 將以可定制的間隔針對線程池中的某個線程調用指定的回調函數(shù)。這意味著你不用輸入請求到 ASP.NET 應用程序便能讓代碼實行,這是一種最合適后臺處理的情形。你也可以在這種后臺處理模式中進行例如索引或發(fā)送電子郵件這樣的工作。
盡管如此,這個技術存在幾個問題,如果你的應用程序域關閉,該定時器實例將停止觸發(fā)其事件。另外,由于 CLR 有一個硬坎,即每個進程的線程數(shù)是固定的,你便可能陷入嚴重的服務器負荷當中,此時可能就沒有線程來處理定時器,從而造成延時。為了讓發(fā)生這種情況的幾率最小化,ASP.NET 通過在進程中預留一定數(shù)量的空閑線程,并只使用部分線程來處理請求。然而,如果你有許多異步處理,這樣做會有問題。
由于篇幅所限,在此無法列出代碼,但你可以從 http://www.rob-howard.net/ 下載可消化的例子。其中有 Blackbelt TechEd 2004 展示的幻燈和 Demo。
技巧 7——頁面輸出緩存和代理服務器
ASP.NET 是你的表示層(或者說應該是);它由頁面,用戶控件,服務器控件(HttpHandlers and HttpModules)以及它們生成的內(nèi)容組成。如果你有一個產(chǎn)生輸出的 ASP.NET 頁面,不管是輸出 HTML,XML,圖像還是任何其它數(shù)據(jù),而且每個請求你都運行這個代碼并產(chǎn)生相同的輸出,此時最好選擇使用頁面輸出緩存。
只要在頁面頂部添加這一行代碼即可:
<%@ Page OutputCache VaryByParams="none" Duration="60" %>
你可以為此頁面有效地產(chǎn)生一次輸出并可以在60秒內(nèi)多次重用它,一到這個時間點,該頁面將重新執(zhí)行并將再次將輸出添加到 ASP.NET Cache。這個行為還能用某些低級編程 APIs 來完成。輸出緩存有幾個可以配置的設置,比如:VaryByParams 屬性。VaryByParams 不是必須的,但允許你指定 HTTP GET 或 HTTP POST 參數(shù)來改變緩存入口。例如,default.aspx?Report=1 或 default.aspx?Report=2 可以簡單地設置 VaryByParam="Report" 來對輸出進行緩存。額外的參數(shù)被命名并用用分號分隔。
在使用輸出緩存機制時,許多人都不了解 ASP.NET 頁還產(chǎn)生一組下游緩存服務器 HTTP 頭,比如 Microsoft Internet Security and Acceleration Server 或 Akamai 使用的 HTTP 頭。當設置 HTTP 緩存頭,文檔可以被緩存到這些網(wǎng)絡資源,從而響應客戶端請求不必返回原服務器。
然而,使用頁面輸出緩存并不會使你的應用程序更有效率,但它能通過下游緩存技術緩存文檔從而潛在地降低服務器的負載。當然,這只能是異步內(nèi)容;一旦實施下游緩存,你將無法看到任何請求,也不能實現(xiàn)身份認證來防止對它的存取。
技巧 8——運行 IIS 6.0 (如果僅用于內(nèi)核緩存)
如果你不運行 IIS 6.O(Windows Server 2003),那么你將得不到微軟 Web 服務器中一些重大的性能改進。在技巧 7 中,我談到了輸出緩存。在 IIS 5.0 中,請求到達 IIS,然后到達 ASP.NET。當使用緩存時,ASP.NET 中的 HttpModule 接受該請求,并從該緩存中返回內(nèi)容。
如果你用 IIS 6.0,有一些巧妙的特性叫內(nèi)核緩存,它不需要將任何代碼改成 ASP.NET。當 ASP.NET對請求進行緩存處理,IIS 內(nèi)核緩存便接收一份緩存數(shù)據(jù)的拷貝。當請求來自網(wǎng)絡驅動器,內(nèi)核一級的驅動程序(沒有到用戶模式的上下文轉換)接收該請求,如果緩存,則直接用緩存數(shù)據(jù)響應并完成執(zhí)行。這意味著當你使用 IIS 內(nèi)核模式緩存和 ASP.NET 緩存時,你將看到無法置信的性能結果。在開發(fā) Visual Studio 2005 的 ASP.NET 期間,我是負責 ASP.NET 性能的程序經(jīng)理。開發(fā)人員的工作做的真是棒極了,而我基本上每天都在看報告。內(nèi)核模式緩存結果總是最有趣的。典型的情況是請求/響應往往使網(wǎng)絡飽和,但 IIS 的運行僅占 CPU 的百分之五。真令人驚異!當然使用 IIS 6.O 有其它一些原因,但內(nèi)核模式緩存是顯而易見的理由。
技巧 9——使用 Gzip 壓縮
雖然使用 gzip 壓縮不是一個必須的服務器性能技巧(因為你可能看到 CUP 的使用率上升了),但它能降低服務器發(fā)送字節(jié)的數(shù)量。從而感覺頁面更快,而且減少帶寬的占用。其壓縮的效果好壞取決于所發(fā)送的數(shù)據(jù)以及客戶端瀏覽器是否支持這種壓縮(IIS 只會將數(shù)據(jù)發(fā)送到支持 gzip 的瀏覽器,比如:IE 6.0 和 Firefox),從而使服務器可以在每秒鐘里處理更多的請求。事實上,只要你降低返回數(shù)據(jù)的數(shù)量,便能提高每秒所處理的請求數(shù)。
有一個好消息是 gzip 壓縮是 IIS 6.0 的內(nèi)建特性,并且比它在 IIS 5.0 中使用的效果更好。但是,要想在 IIS 6.0 中啟用 gzip 壓縮可能沒那么方便,IIS 的屬性對話框里找不到設置它的地方。IIS 團隊將卓越的 gzip 壓縮能力內(nèi)建在服務器中,但忽視了建立一個啟用壓縮特性的管理用戶界面。要想啟用 gzip 壓縮機制,你必須深入到 IIS 的 XML 配置設置內(nèi)部(必須對之相當熟悉才能配置)。順便提一下,在此感謝 OrcsWeb 的 Scott Forsyth 幫我解決了在 OrcsWeb 數(shù)個 http://www.asp.net/ 服務器上的這個問題。
與其在本文中包含整個過程,還不如閱讀 Brad Wilson 在 IIS6 Compression 上的文章。微軟知識庫也有一篇關于為ASPX啟用壓縮特性的文章:Enable ASPX Compression in IIS。但是,還必須注意一點,動態(tài)壓縮與內(nèi)核緩存由于某些實現(xiàn)細節(jié)的原因,其在 IIS 6.0 中是相互排斥的。
技巧 10——服務器控件的可視狀態(tài)
可視狀態(tài)(View State)對于 ASP.NET 來說是個奇特的名字,它在所產(chǎn)生的頁面中隱藏輸入域以存儲某些狀態(tài)數(shù)據(jù)。當頁面被發(fā)回服務器,該服務器能解析,檢查其有效性并將這個狀態(tài)數(shù)據(jù)應用到頁面的控件樹中??梢暊顟B(tài)是一種非常強大的能力,因為它允許狀態(tài)被客戶端持續(xù)化并且它不需要cookies 或 服務器內(nèi)存來存儲該狀態(tài)。許多 ASP.NET 服務器控件使用可視狀態(tài)來持續(xù)化與頁面元素交互期間所作的設置,例如,對數(shù)據(jù)進行分頁時保存當前頁顯示頁。
然而,使用可視狀態(tài)有許多不利之處,首先,不論是在請求的時候還是提供服務的時候,它都增加造成整個頁面的負擔。當序列化或反序列化被返回服務器的可視狀態(tài)數(shù)據(jù)時還產(chǎn)生一些附加的開銷。最終可視狀態(tài)會增加服務器的內(nèi)存分配。
最著名的服務器控件要數(shù) DataGrid 了,使用可視狀態(tài)有過之而無不及,即便是在不需要使用的時候也是如此。ViewState 屬性默認是啟用的,但如果你不需要它,可以在頁面控件級或頁面級關閉它。在某個控件中,只要將 EnableViewState 設置為 false,或者在頁面里使用如下全局設置:
<%@ Page EnableViewState="false" %>
如果在某頁面中不進行回發(fā),或每次請求頁面時總是重新產(chǎn)生控件,那么你應該在頁面級禁用可視狀態(tài)。
結論
我已經(jīng)向你提供了一些我認為有用的編寫高性能 ASP.NET 應用程序的技巧。正如我在本文開頭時所講的那樣,這是一些很初級的指南,而不是 ASP.NET 性能方面的最終定論。(更多有關改進 ASP.NET 應用程序性能方面的信息請參見:Improving ASP.NET Performance.)只有通過自己的經(jīng)驗方能找到最佳途徑來解決具體的性能問題。不管怎樣,在你解決問題的過程中,這些技巧多少會對你有所裨益的。在軟件開發(fā)過程中,每一個應用都有其獨特的一面,沒有什么東西是絕對的。
——常見的性能神話
最常見的神話之一是 C# 代碼比 Visual Basic 代碼快。這樣的說法是站不住腳的,雖然在 Visual Basic 中存在一些 C# 沒有的性能阻礙行為,比如顯式地聲明類型。但是如果遵循良好的編程實踐,沒有理由說明 Visual Basic 和 C# 代碼不能以幾乎同樣的性能執(zhí)行。簡單說來,相同的代碼產(chǎn)生相同的結果。
另一個神話是后臺代碼比內(nèi)聯(lián)代碼快,這是絕對不成立的。性能與你的 ASP.NET 應用程序代碼在哪沒有什么關系,無論是后臺代碼文件還是內(nèi)聯(lián)在 ASP.NET 頁面。有時我更喜歡使用內(nèi)聯(lián)代碼,因為變更不會產(chǎn)生后臺代碼那樣的更新成本。例如,使用后臺代碼必須更新整個后臺 DLL,那時一個可能引起驚慌的主張。
第三個神話是組件比頁面要快。這在經(jīng)典的 ASP 中是存在的,因為編譯的 COM 服務器要比 VBScript 快得多。但是對于頁面和組件都是類的 ASP.NET 來說則不然。不論你的代碼是以后臺代碼形式內(nèi)聯(lián)在頁面,還是分離的組件,所產(chǎn)生的性能差別不大。只是這種組織形式能更好地從邏輯上對功能進行分組,在性能上沒有差別。
我想澄清的最后一個神話是用 Web 服務來實現(xiàn)兩個應用程序之間各個功能。Web 服務應該被用于連接異構系統(tǒng)或提供系統(tǒng)功能及行為的遠程訪問。不應該將它用于兩個相同系統(tǒng)的內(nèi)部連接。雖然使用起來很容易,但有很多其它更好的可選方法。最糟的事情莫過于將 Web 服務用于相同服務器上 ASP 和 ASP.NET 應用程序之間的通訊,我已經(jīng)不厭其煩地對之進行了說明。