2011年11月5日星期六

【高性能web開發】 ASP.NET Web伺服器

本文通過一個特別的案例:最終用戶使用流覽器向伺服器請求包含100條最新新聞紀錄的頁面,慢慢的展開。

本文集中在ASP.NET Web伺服器(特指用於接收使用者請求,處理業務邏輯和回應HTML的伺服器; 分散式,用戶端,IIS,資料庫和應用伺服器配置和優化部分,稍後介紹)

應用程式級別
1.生產環境使用Release版本,而不是Debug版本
•關閉所有調試日誌和資訊
•移除所有用於調試,測試和跟蹤的代碼
•使用宏操作可以很方便的關閉和管理這些代碼

#if DEBUG
Console.WriteLine("");//日誌?輸出?調試?#endif

•配置Web.Config關閉調試模式
2.移除不必要的HTTP Module
•通過運行時訪問HttpContext.Current.ApplicationInstance.Modules確定使用到的Module
•通過web.config移除不必要的HTTP Module
•例如如果你不想使用Session那麼就移除SessionModule •  本案例中,由於新聞資料和使用者無關,也許我們可以移除很多Module


3.移除不必要的檔
•特別是沒用的dll和PDB檔

4.啟用伺服器配置
•例如GC運行於伺服器模式

5.檢查Global等全域性代碼和功能


頁面級別
1.考慮使用比Aspx羽量級的處理模式,例如ashx(httphandler)
•例如需要一個頁面作為介面提供或者接收資料,(而不是返回一堆HTML)
•aspx頁面生命週期過於笨重,速度緩慢
•aspx內部經常使用伺服器控制項,這也是重量級的主
•aspx內部伺服器控制項產生的ViewState等。。。。
•個人比較喜歡MVC做頁面和WCF做服務

2.避免使用重量級解決方案
•Asp.net UpdatePanel (這東西搞搞管理員模組也就算了,就不要拿到最終使用者介面來嚇人了)
•EXT,ComponentArt等重量級協力廠商解決方案

3.避免太深的頁面或者類繼承(例如APage:BPage....)
•不要設計超級父類,集中了一大堆功能
•繼承級別過深是較為消耗性能的

4.優化邏輯
•例如已經在PageLoad中初始化過的物件,不要在例如按鈕點擊等事件中再初始化一次
•例如不要有這種代碼。。。

DataSet ds = new DataSet();// 無奈的初始化。。。。偶爾還能看到非常重量級的初始化 ds = ClassA.LoadDataSet();

5.小心使用重量級資源,包括但不僅限於以下內容
•Thread
•Sesssion
•Application
•內核鎖
•記憶體
•大量小物件 (GC壓力,例如大量的使用小字串),可以使用WinDbg+SOS調查

6.設計往往對與性能有至關重要的影響
•例如長時間的操作,非同步比同步性能要好很多
•例如大批量同類的操作,批量操作比一次操作一條要好的多
•CPU未滿,而且希望縮短回應時間,考慮多執行緒
•CPU滿了 考慮空間換時間
•CPU沒滿 考慮時間換空間 (注意 IO壓力大也會導致CPU100% 這個時候還是考慮空間換時間)
•例如設計時候考慮資料和樣式分離,每次只要重新拉資料就行了例如每次拉取資料的時候,伺服器只返回已經更新了的資料
•例如按照更新時間排列,如果更新了早一些的資料;那麼每次讀取的時候按照更新時間排列就是一個問題,不如在記憶體中保存最新的100條資料,有更新的話直接把該集合中最舊的一條移除,最新的一條插入 (可以多留幾條備用)
•這個例子一般是讀多寫少,讀寫比例可能達到1000:1,是很好的使用緩存的例子

緩存
1.頁面緩存和頁面片段緩存
<%@ OutputCache Duration="60" VaryByParam="none"%>

Using the Output Cache

Last generated on:
2.適當的使用使用304緩存,或者流覽器端緩存

3.使用HttpRuntimeCache緩存資料,或使用靜態物件緩存資料
•如果不需要控制過期時間,或者永不過期,建議用靜態物件緩存資料,Cache其實還是很重量的

4.就這個方案而言
•可以把這100條資料放在記憶體中,如果有更新的時候直接更新記憶體,需要讀取的時候直接從記憶體中讀取
•緩存的同步和更新永遠都是一個大問題,選擇不同的緩存取決於你所想要的性能和能承受的缺陷 (例如如果能接受1分鐘的的資料延遲,緩存)
•良好的執行緒同步知識,可以減少很多的BUG


代碼級別
1.將可能的類設計為封閉的(不可繼承)
•例如新聞的實體類

2.使用更為有效率的方法
•例如使用Request.QueryString[Key] 而不是Request[Key]
•例如Int.TryParse,而不是Int.Parse
•謹慎使用異常