【高性能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"%>
<html>
<script language="C#" runat="server">
void Page_Load(Object sender, EventArgs e)
{
TimeMsg.Text = DateTime.Now.ToString("G");
}
</script>
<body>
<h3>Using the Output Cache</h3>
<p>Last generated on: <asp:label id="TimeMsg" runat="server"/>
</body>
</html>
2.適當的使用使用304緩存,或者瀏覽器端緩存
3.使用HttpRuntimeCache緩存數據,或使用靜態對象緩存數據
- 如果不需要控制過期時間,或者永不過期,建議用靜態對象緩存數據,Cache其實還是很重量的
4.就這個方案而言
- 可以把這100條數據放在內存中,如果有更新的時候直接更新內存,需要讀取的時候直接從內存中讀取
- 緩存的同步和更新永遠都是一個大問題,選擇不同的緩存取決于你所想要的性能和能承受的缺陷 (例如如果能接受1分鐘的的數據延遲,緩存)
- 良好的線程同步知識,可以減少很多的BUG
代碼級別
1.將可能的類設計為封閉的(不可繼承)
- 例如新聞的實體類
2.使用更為有效率的方法
- 例如使用Request.QueryString[Key] 而不是Request[Key]
- 例如Int.TryParse,而不是Int.Parse
- 謹慎使用異常
慢慢補充,未完待續。。。。。。
使用工具測量是很重要的一個方面,例如DotTrace,Visual Studio 2010性能測試工具,LoadRunner等壓力測試工具
但是此類工具無法彌補基本概念上的缺失,例如封閉類的性能會比非封閉類的性能好 (在這個方面FxCop之類的工具可以稍微幫你一下) ,
但是工具可以幫你較為準確的測量修改后的性能,最終跑出來的性能才是決定一個優化的價值的關鍵
此外高性能一般和可維護性是沖突的,需要找一個平衡點
本人水平有限,希望大家一起討論
浙公網安備 33010602011771號