瀏覽量比較大的網站應該從哪幾個方面入手?

當然,提問前先將個人的一些理解分享。大家有的也請不吝共享,偶急切的需要這方面的經驗....

下面所提到的主要是針對一般的網站,不包括下載或聊天室等特殊站點...

一、減少數據庫的壓力

  緩存查詢結果/建
內存

二、 減少Apache的壓力——減少HTTP的請求次數

  背景圖片全部做成一張然後用CSS控制位置/不使用AJAX來進行即時驗證(不考慮客戶體驗什麼的,通過拖長客戶時間來減輕
服務器壓力)

三、減輕I/O壓力

  頁面局部緩存 

 

編寫高性能Web網站應用程序入門的10個技巧 

用 ASP.NET 編寫 Web 應用程序其輕鬆程度令人難以置信。它是如此的容易,以至於許多開發人員不用花費多少時間來構築其應用便能獲得非常好的性能。在本文中,我將給出10個編寫高性能 Web 應用的技巧。我的評論不僅僅侷限與 ASP.NET 應用,因爲它們只是 Web 應用的一個子集。本文也不是 Web 應用性能調整的權威指南——這方面的內容可以寫成一本書。相反,本文可以被視作一個好的起點。

  在廢寢忘食地工作之前,我常常要去攀巖。在攀巖之前,我總是要看一下指南手冊中的線路並閱讀以前來此一遊的人留下的建議和忠告。但是,不管指南手冊有多磨好,在嘗試一次特定的具有挑戰性的攀爬之前,你都必須付諸實際的行動。同樣,在你面臨解決的性能問題或者營運一個高吞吐量的站點之前,你只能想方設法編寫高性能 Web 應用程序。
  我們個人經驗來自在微軟 ASP.NET 團隊從事底層架構程序經理,運行和管理
www.asp.net ,並協助架構 Community Server 過程中的經歷,Community Server 是幾個有名的 ASP.NET 應用程序的下一個版本(它將 ASP.NET Forums,.Text 和 nGallery 整合到一個平臺)。我確信這些幫助過我的技巧也會對你有所裨益。
  你應該考慮將應用程序分離成幾個邏輯層。你可能聽說過術語3-層(或n-層)物理體系結構。它們通常是跨進程和/或硬件對功能進行物理劃分的規定的體系結構模式。當系統需要伸縮時,更多的硬件能被添加。然而,總是應該避免與進程和機器忙碌程度相關的性能問題。所以,不管什麼時候,只要可能,都要在相同的應用中一起運行 ASP.NET 頁面及其相關的組件。
  由於代碼和層之間的邊界分離,使用 Web 服務或遠程調用將降低20%以上的性能。
  數據層則稍微有些不同,因爲數據庫通常都用專門的硬件。但是,數據庫的處理成本仍然很高,因此最優化代碼時,數據層的性能應該是首當其充要關注的地方。
  在着手解決你的應用程序的性能問題之前,一定要剖析應用程序,確定問題之所在。獲取關鍵的性能計數器值(如實現垃圾收集所花時間之百分比的性能計數器的值)對於查找應用程序在何處最耗時也是非常重要的。憑藉直覺常常也能找到耗時所在。
  本文所描述的性能改進有兩種類型:大型優化,如使用 ASP.NET Cache,以及不斷重複進行的微型優化。這些微型優化有時很有意思。對代碼的小小改動便會引起很大的動靜,產生成千次的調用。對於大型優化,你可能會看到整體性能的大跳躍。而對微型優化,給定請求可能只是毫秒級的調整,但按每天的請求總數計算,其結果的改進可能是巨大的。

數據層的性能

  當調整某個應用程序的性能時,有一個簡單的試金石,你可以用它按先後次序:檢查代碼是否存取數據庫?如果是,多長時間存取一次?注意相同的測試也可以被應用於使用 Web 服務或遠程調用的代碼,但我們本文中不涉及這方面內容。
  如果在特定的代碼流程中必須具有對數據庫的請求以及要考察其它方面,如:想對字符串處理進行優先優化,那麼暫且把它放一放,先按照上面定好的優先次序來做。除非你有異乎尋常的性能問題,否則你的時間應該用在嘗試最優化與數據庫的連接所花的時間,返回的數據量以及多長時間往返一次和數據庫的通訊上。
  有了這些概括信息,下面就讓我們來看看能幫助你改善應用程序性能的十個技巧。我將從能獲得最顯著效果的改變開始。

技巧 1 —— 返回多個結果集

  複審你的數據庫代碼,看看是否有多於一次的對數據庫的訪問請求。這樣每次往返數據庫都降低你的應用程序能處理的每秒請求數。通過在單個數據庫請求中返回多結果集,你能降低與數據庫通信的總體時間。同時你也將使系統更具伸縮性,因爲你減少了數據庫服務器處理請求的負擔。
  雖然你可以用動態 SQL 返回多結果集,我更喜歡使用存儲過過程。是否將業務邏輯駐留在存儲過程當中是個有待爭論的問題,但我認爲,如果存儲過程中的邏輯能約束返回的數據(降低數據集的尺寸,在網絡上傳輸的時間以及邏輯層不必過慮數據),這是一件好事情。
  使用 SqlCommand 命令實例及其 ExecuteReader 方法來處理強類型的各個業務類,你通過調用 NextResult 可以向前移動結果集指針。Figure 1 示範了處理幾個帶類型的 ArrayLists 例子會話。從數據庫只返回你需要的數據還會降低服務器內存的分配。

技巧 2 —— 分頁數據存取

  ASP.NET DataGrid 提供了非常好的能力:數據分頁支持。當啓用 DataGrid 中的分頁功能,則每次只顯示固定數量的記錄。此外,分頁用戶界面也會顯示在 DataGrid 底部用於導航記錄。分頁用戶界面允許你向前向後導航所顯示的記錄,一次顯示固定數量的記錄。
  有一個美中不足的是用 DataGrid 分頁需要將所有數據邦定到此柵格控件(gird)。例如,你的數據層必須返回所有數據,然後 DataGrid 將根據當前頁過濾掉所有顯示的記錄。當你通過 DataGrid 進行分頁時,如果有 100,000 條記錄被返回,那麼每個請求有 99,975 條記錄將被廢棄掉(假設頁尺寸爲 25)。當記錄數不斷增加,此應用程序的性能便會遭受痛苦,因爲每次請求所要發送的數據會越來越多。
  編寫較好的分頁代碼的一個好的方法是用存儲過程。Figure 2 示範了一個用 Northwind 數據庫中 Orders 表通過存儲過程分頁的例子。很簡單,只要你在頁面中傳遞索引以及頁尺寸即可。相應的結果集先被計算然後被返回。
  在 Community Server 中,我們編寫了幾個分頁控件來完成數據分頁。你將會看到,我使用了技巧 1 中討論的思路,從一個存儲過程中返回連個結果集:總記錄數和請求的數據。
  返回的總記錄數依賴於所執行的查詢不同而不同。例如,某個 WHERE 子句可被用於約束返回的數據。爲了計算在分頁用戶界面顯示的總頁數,返回的總記錄數必須是已知的。例如,如果有 1,000,000 條記錄,用一個 WHERE 子句對之過濾後爲 1,000 條記錄,則分頁邏輯必須要知道總記錄數以便在分頁用戶界面中正確呈現。

 

技巧 3 —— 連接池

  建立 Web 應用程序與 SQL Server 之間的 TCP 連接是一項昂貴的操作。微軟的開發人員利用連接池技術已經有好長一段時間了,這個技術使他們能重用到數據庫的連接。而不是每次請求都建立新的 TCP 連接,新連接僅在連接池中得不到連接時才建立。當連接被關閉時,它被返回到連接池中,在那裏它仍然保持與數據庫的連接,與完全斷開 TCP 連接相反。
  當然,你需要提防泄漏的連接。當你處理完畢,一定要關閉連接。重申一次:不管人們怎麼吹噓微軟 .NET 框架中的垃圾收集特性,每當你處理完畢,一定要顯式地調用連接對象的 Close 或 Dispose 方法。不要指望公共語言運行時(CLR)來爲你定時清除和關閉連接。CLR 最終將銷燬類並強行關閉連接,但你無法保證該對象的垃圾收集屆時會起作用。
  爲了充分用好連接池,有幾條規則必須瞭然於心。首先,打開連接,進行處理,然後關閉連接。寧願每個請求的連接打開和關閉多次,也不要保持連接打開狀態以及在不同的方法間將它傳來傳去。其次,使用相同的連接串(如果你使用集成身份檢查,那麼也要用相同的線程身份)。如果你不用相同的連接串,例如,根據登錄用戶來定製連接串,你將無法得到連接池所提供的相同的最優化值。當模擬大用戶量情形時,如果你使用集成身份檢查,那麼你的連接池將效力大減。.NET CLR 數據性能計數器在試圖跟蹤任何與連接池有關的性能問題時是非常有用的。
  不管什麼時候,只要你的應用程序連接到運行在其它進程中的資源,比如某個數據庫,你都應該針對連接到資源所耗時間,發送和接收數據所耗時間以及往返次數進行優化。爲了實現較好的性能,應該首當其充優化應用程序中任何種類的忙碌進程。
  應用層包含到數據層的連接以及將數據轉換成有意義的類實例和業務處理的邏輯。以 Community Server 爲例,你要在其中處理 Forums 和 Threads 集合;以及應用許可這樣的業務規則;尤其重要的是緩衝(Caching)邏輯也實現其中。

技巧 4 —— ASP.NET Cache API

  在編寫代碼之前要做的頭等大事之一是最大限度地構建應用層併發掘 ASP.NET 的 Cache 特性。
  如果你的組件在 ASP.NET 應用程序內運行,那麼你只需要在應用程序工程中引用 System.Web.dll 即可。當你需要訪問 Cache 時,用 HttpRuntime.Cache 屬性(相同的對象也可以通過 Page.Cache 和 HttpContext.Cache 訪問)。
  緩衝數據有幾個準則。首先,如果數據能被使用多次,緩衝是個好的後選方案。其次,如果數據對給定請求或用戶是一般的數據而非專用數據,那麼最好是選擇緩衝。如果數據用戶或請求專用,如果需要保存期很長但可能不被經常使用,那麼仍然要用緩衝。第三,常常被忽略的一個準則是有時緩衝太多的東西。一般來說,在x86機器上,爲了降低內存不足錯誤的機率,運行某個進程不要超過800MB私有字節。因此,緩衝應該有個上限。換句話說,你也許能重用某個計算的結果,但如果該計算有10個參數,你可能試圖針對10個置換進行緩衝,這樣做可能會給你帶來麻煩。ASP.NET 提供的最常見的容錯是由覆蓋緩衝導致的內存不足錯誤,尤其是大型數據集。
  Cache 有幾個重要特性是必須要瞭解的。第一個是 Cache 實現了最近最少使用(least-recently-used)算法,允許 ASP.NET 強制 Cache 清除操作 —— 如果可用內存下降到低水平 —— 則自動從 Cache 中刪除不使用的項目。第二個是 Cache 支持依賴性到期特性,它能強制包括時間,鍵值,文件失效。時間常常被使用,但 ASP.NET 2.0 引入了具有更強大的失效類型:數據庫緩衝失效。也就是當數據庫中的數據改變時,緩衝中的條目會自動刪除。有關數據庫緩衝失效的更多信息參見 Dino Esposito 在 MSDN 雜誌 2004 年七月刊的 Cutting Edge 專欄文章。該緩衝的體系結構,參見 圖 3。

Figure 3 ASP.NET Cache

技巧 5 —— 預請求緩衝(Per-Request Caching)

  在本文前面,我曾提到對頻繁執行的代碼塊所做的小小改動可能產生很大的,整體性能的提升。我把其中一個我特別中意的叫做預請求緩衝(per-request caching)。
  由於 Cache API 被設計用來緩衝長期數據或直到某個條件被滿足,預請求緩衝意旨用於請求期間的緩衝該數據。特定的代碼流程被每次請求頻繁訪問但是數據只需要被拾取,應用,修改或更新一次,這樣說太理論化,還是讓我們看一個具體的例子吧。
在 Community Server 的 Forums (論壇)應用中,某個頁面上使用的每個服務器控件需要個性化數據以確定使用那個皮膚和式樣頁,以及其它的個性化數據,其中有些數據可以被長時間緩衝,但有些數據,比如用於控件的皮膚在單個請求中只被拾取一次並在該請求執行期間被重用多次。
  爲了完成預請求緩衝,用 ASP.NET HttpContext。HttpContext 的實例是隨每個請求創建的,並可以通過 HttpContext.Current 屬性在那個請求執行期間的任何地方存取它。HttpContext 類具有一個特別的 Items 集合屬性,被添加到該 Items 集合的對象和數據只是在該請求期間被緩存。就像你可以使用 Cache 來保存頻繁使用的數據一樣,你可以用 HttpContext.Items 來保存只在某個預請求中使用的數據。在此背景後的邏輯很簡單:當數據不存在時被添加到 HttpContext.Items 集合,以及在隨後的併發查找中簡單地返回 HttpContext.Items 中發現的數據。

技巧 6——後臺處理

  你的代碼流程應該儘可能快,對吧?你自己可能多次發現要完成每個請求或每n個請求的任務代價很高。發出 e-mail 或解析並檢查輸入數據的有效性就是個例。
  在重新生成 ASP.NET Forums 1.0 並把它整合到 Community Server 時,我們發現添加新貼的代碼流程非常慢。每次添加帖子,應用程序首先要確保沒有重複貼,然後必須用“badword”過濾器解析該貼的表情圖像,記號並索引,如果必要還要將帖子添加到相應的隊列中,對附件進行有效性檢查,最終完成發貼後,給預訂者發出 e-mail 通知。顯然,這裏做的工作太多。
  我們發現大多數時間都花在了索引邏輯和發送e-mail上。索引帖子是一個很耗時的操作,此外,內建的 System.Web.Mail 功能要與 SMTP 服務器連接並順序發送郵件。當特定帖子或主題預定者數量增加時,AddPost 函數的執行時間會越來越長。
  並不是每個請求都需要索引郵件,我們想最好是批量集中處理,並且一次只索引25個帖子或每隔五分鐘發送一次郵件。我們決定使用的代碼與我曾在原型數據庫緩衝失效中所使用的代碼相同,最終它也被納入 Visual Studio 2005。
  名字空間 System.Threading 中的 Timer 類非常有用,但在.NET 框架中鮮爲人知,至少對 Web 開發者來說是這樣。一旦創建,Timer 將以可定製的間隔針對線程池中的某個線程調用指定的回調函數。這意味着你不用輸入請求到 ASP.NET 應用程序便能讓代碼實行,這是一種最合適後臺處理的情形。你也可以在這種後臺處理模式中進行例如索引或發送電子郵件這樣的工作。
  儘管如此,這個技術存在幾個問題,如果你的應用程序域關閉,該定時器實例將停止觸發其事件。另外,由於 CLR 有一個硬坎,即每個進程的線程數是固定的,你便可能陷入嚴重的服務器負荷當中,此時可能就沒有線程來處理定時器,從而造成延時。爲了讓發生這種情況的機率最小化,ASP.NET 通過在進程中預留一定數量的空閒線程,並只使用部分線程來處理請求。然而,如果你有許多異步處理,這樣做會有問題。
  由於篇幅所限,在此無法列出代碼,但你可以從
http://www.rob-howard.net/ 下載可消化的例子。其中有 Blackbelt TechEd 2004 展示的幻燈和 Demo。

技巧 7——頁面輸出緩存和代理服務器

  ASP.NET 是你的表示層(或者說應該是);它由頁面,用戶控件,服務器控件(HttpHandlers and HttpModules)以及它們生成的內容組成。如果你有一個產生輸出的 ASP.NET 頁面,不管是輸出 HTML,XML,圖像還是任何其它數據,而且每個請求你都運行這個代碼併產生相同的輸出,此時最好選擇使用頁面輸出緩存。
只要在頁面頂部添加這一行代碼即可:

<%@ Page OutputCache VaryByParams="none" Duration="60" %>
  你可以爲此頁面有效地產生一次輸出並可以在60秒內多次重用它,一到這個時間點,該頁面將重新執行並將再次將輸出添加到 ASP.NET Cache。這個行爲還能用某些低級編程 APIs 來完成。輸出緩存有幾個可以配置的設置,比如:VaryByParams 屬性。VaryByParams 不是必須的,但允許你指定 HTTP GET 或 HTTP POST 參數來改變緩存入口。例如,default.aspx?Report=1 或 default.aspx?Report=2 可以簡單地設置 VaryByParam="Report" 來對輸出進行緩存。額外的參數被命名並用用分號分隔。
  在使用輸出緩存機制時,許多人都不瞭解 ASP.NET 頁還產生一組下游緩存服務器 HTTP 頭,比如 Microsoft Internet Security and Acceleration Server 或 Akamai 使用的 HTTP 頭。當設置 HTTP 緩存頭,文檔可以被緩存到這些網絡資源,從而響應客戶端請求不必返回原服務器。
  然而,使用頁面輸出緩存並不會使你的應用程序更有效率,但它能通過下游緩存技術緩存文檔從而潛在地降低服務器的負載。當然,這隻能是異步內容;一旦實施下游緩存,你將無法看到任何請求,也不能實現身份認證來防止對它的存取。

技巧 8——運行 IIS 6.0 (如果僅用於內核緩存)

  如果你不運行 IIS 6.O(Windows Server 2003),那麼你將得不到微軟 Web 服務器中一些重大的性能改進。在技巧 7 中,我談到了輸出緩存。在 IIS 5.0 中,請求到達 IIS,然後到達 ASP.NET。當使用緩存時,ASP.NET 中的 HttpModule 接受該請求,並從該緩存中返回內容。
  如果你用 IIS 6.0,有一些巧妙的特性叫內核緩存,它不需要將任何代碼改成 ASP.NET。當 ASP.NET對請求進行緩存處理,IIS 內核緩存便接收一份緩存數據的拷貝。當請求來自網絡驅動器,內核一級的驅動程序(沒有到用戶模式的上下文轉換)接收該請求,如果緩存,則直接用緩存數據響應並完成執行。這意味着當你使用 IIS 內核模式緩存和 ASP.NET 緩存時,你將看到無法置信的性能結果。在開發 Visual Studio 2005 的 ASP.NET 期間,我是負責 ASP.NET 性能的程序經理。開發人員的工作做的真是棒極了,而我基本上每天都在看報告。內核模式緩存結果總是最有趣的。典型的情況是請求/響應往往使網絡飽和,但 IIS 的運行僅佔 CPU 的百分之五。真令人驚異!當然使用 IIS 6.O 有其它一些原因,但內核模式緩存是顯而易見的理由。

技巧 9——使用 Gzip 壓縮

  雖然使用 gzip 壓縮不是一個必須的服務器性能技巧(因爲你可能看到 CUP 的使用率上升了),但它能降低服務器發送字節的數量。從而感覺頁面更快,而且減少帶寬的佔用。其壓縮的效果好壞取決於所發送的數據以及客戶端瀏覽器是否支持這種壓縮(IIS 只會將數據發送到支持 gzip 的瀏覽器,比如:IE 6.0 和 Firefox),從而使服務器可以在每秒鐘裏處理更多的請求。事實上,只要你降低返回數據的數量,便能提高每秒所處理的請求數。
  有一個好消息是 gzip 壓縮是 IIS 6.0 的內建特性,並且比它在 IIS 5.0 中使用的效果更好。但是,要想在 IIS 6.0 中啓用 gzip 壓縮可能沒那麼方便,IIS 的屬性對話框裏找不到設置它的地方。IIS 團隊將卓越的 gzip 壓縮能力內建在服務器中,但忽視了建立一個啓用壓縮特性的管理用戶界面。要想啓用 gzip 壓縮機制,你必須深入到 IIS 的 XML 配置設置內部(必須對之相當熟悉才能配置)。順便提一下,在此感謝 OrcsWeb 的 Scott Forsyth 幫我解決了在 OrcsWeb 數個
http://www.asp.net/ 服務器上的這個問題。
  與其在本文中包含整個過程,還不如閱讀 Brad Wilson 在 IIS6 Compression 上的文章。微軟知識庫也有一篇關於爲ASPX啓用壓縮特性的文章:Enable ASPX Compression in IIS。但是,還必須注意一點,動態壓縮與內核緩存由於某些實現細節的原因,其在 IIS 6.0 中是相互排斥的。

技巧 10——服務器控件的可視狀態

  可視狀態(View State)對於 ASP.NET 來說是個奇特的名字,它在所產生的頁面中隱藏輸入域以存儲某些狀態數據。當頁面被髮回服務器,該服務器能解析,檢查其有效性並將這個狀態數據應用到頁面的控件樹中。可視狀態是一種非常強大的能力,因爲它允許狀態被客戶端持續化並且它不需要cookies 或 服務器內存來存儲該狀態。許多 ASP.NET 服務器控件使用可視狀態來持續化與頁面元素交互期間所作的設置,例如,對數據進行分頁時保存當前頁顯示頁。
  然而,使用可視狀態有許多不利之處,首先,不論是在請求的時候還是提供服務的時候,它都增加造成整個頁面的負擔。當序列化或反序列化被返回服務器的可視狀態數據時還產生一些附加的開銷。最終可視狀態會增加服務器的內存分配。
  最著名的服務器控件要數 DataGrid 了,使用可視狀態有過之而無不及,即便是在不需要使用的時候也是如此。ViewState 屬性默認是啓用的,但如果你不需要它,可以在頁面控件級或頁面級關閉它。在某個控件中,只要將 EnableViewState 設置爲 false,或者在頁面裏使用如下全局設置:

<%@ Page EnableViewState="false" %>
  如果在某頁面中不進行回發,或每次請求頁面時總是重新產生控件,那麼你應該在頁面級禁用可視狀態。

結論

  我已經向你提供了一些我認爲有用的編寫高性能 ASP.NET 應用程序的技巧。正如我在本文開頭時所講的那樣,這是一些很初級的指南,而不是 ASP.NET 性能方面的最終定論。(更多有關改進 ASP.NET 應用程序性能方面的信息請參見:Improving ASP.NET Performance.)只有通過自己的經驗方能找到最佳途徑來解決具體的性能問題。不管怎樣,在你解決問題的過程中,這些技巧多少會對你有所裨益的。在軟件開發過程中,每一個應用都有其獨特的一面,沒有什麼東西是絕對的。

——常見的性能神話

  最常見的神話之一是 C# 代碼比 Visual Basic 代碼快。這樣的說法是站不住腳的,雖然在 Visual Basic 中存在一些 C# 沒有的性能阻礙行爲,比如顯式地聲明類型。但是如果遵循良好的編程實踐,沒有理由說明 Visual Basic 和 C# 代碼不能以幾乎同樣的性能執行。簡單說來,相同的代碼產生相同的結果。
  另一個神話是後臺代碼比內聯代碼快,這是絕對不成立的。性能與你的 ASP.NET 應用程序代碼在哪沒有什麼關係,無論是後臺代碼文件還是內聯在 ASP.NET 頁面。有時我更喜歡使用內聯代碼,因爲變更不會產生後臺代碼那樣的更新成本。例如,使用後臺代碼必須更新整個後臺 DLL,那時一個可能引起驚慌的主張。
  第三個神話是組件比頁面要快。這在經典的 ASP 中是存在的,因爲編譯的 COM 服務器要比 VBScript 快得多。但是對於頁面和組件都是類的 ASP.NET 來說則不然。不論你的代碼是以後臺代碼形式內聯在頁面,還是分離的組件,所產生的性能差別不大。只是這種組織形式能更好地從邏輯上對功能進行分組,在性能上沒有差別。
  我想澄清的最後一個神話是用 Web 服務來實現兩個應用程序之間各個功能。Web 服務應該被用於連接異構系統或提供系統功能及行爲的遠程訪問。不應該將它用於兩個相同系統的內部連接。雖然使用起來很容易,但有很多其它更好的可選方法。最糟的事情莫過於將 Web 服務用於相同服務器上 ASP 和 ASP.NET 應用程序之間的通訊,我已經不厭其煩地對之進行了說明。

1


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章