學習筆記 --- 緩存、動態頁面靜態化、網站優化

http://www.cnblogs.com/cs_net/archive/2011/03/17/1986620.htmlhtml

1、緩存程序員

緩存(Cache)技術在軟件開發過程當中有着普遍的用途, 它對提高軟件性能和改善客戶體驗有很大幫助. 所謂緩存, 是指將那些常常重複的操做結果暫時存放起來, 在之後的執行過程當中, 只要使用前面的暫存結果便可. 緩存技術在平常生活中隨處可見, 就拿排隊買票來講吧: 買票時須要先排隊, 等輪到本身了, 再告訴售票員你須要買那裏的票, 售票員查詢完後告訴你有仍是沒有. 如有, 付款走人; 若沒有, 離開隊列. 例如, 北京到上海是熱門線路, 票很快就賣完了, 可是根據購票的規則, 仍然有許多乘客會先排隊, 而後問有沒有北京到上海的票, 結果只能默默離開隊列. 若是每一個人都問一遍的話, 售票員估計得煩死. 因而, 售票員在窗口上掛個牌子, 寫上」北京到上海(無票)」, 以後再要購買該票的乘客看到這個牌子就不會在隊列中繼續等待了. 這就是生活中的一個緩存事例.web

 

因而可知, 緩存的思想就是: 儘可能減小執行那些費時的、須要常常重複執行的工做的次數. 在計算機的世界中, 執行速度最慢的操做就是IO操做, 也就是讀寫硬盤的操做. 所以, 構建一個高效的桌面應用程序或網站應用程序時, 很重要的一個因素就是儘量少的減小IO操做. 好比, 相同的數據沒有必要每次都從數據庫中讀取, 在第一次讀取出來後緩存起來便可, 第二次就不須要在作IO操做了.sql

 

那麼在咱們開發Web網站的過程當中, 到底有多少工做能夠採用用緩存呢? 或者說, 咱們能夠在哪些地方使用緩存呢? 見下圖: 下圖是客戶端瀏覽器和Web服務器之間的一次完整的通訊過程, 紅色圓圈標示了能夠採用緩存的地方.數據庫

關於上圖, 若是一邊描述請求過程, 一邊描述緩存可能會不便理解. 像作手術同樣, 這裏首先弄清楚整個請求過程, 而後從總體上對關鍵節點設置的緩存進行描述.編程

 

上圖所示的請求過程:api

客戶端瀏覽器首先發送HttpRequest請求到IIS服務器, 固然也有一些企業內部網絡或者小型機關網絡中會設置代理服務器. 代理服務器在請求過程當中能夠看做是一個轉發請求的中繼器, 或者乾脆在這裏忽略代理服務器也行.數組

IIS服務器接受到請求後, 會根據請求地址進行判斷, 例如: *.html、*.jpg等靜態內容的資源, 會直接由IIS尋找資源並返回給客戶端瀏覽器; 例如: *.aspx、*.ashx、*.asmx等asp.net中的動態內容會交給aspnet_isapi.dll處理.瀏覽器

aspnet_isapi.dll將請求轉交給asp.net組件處理, 這時asp.net組件會啓動一個asp.net的進程(進程名稱爲w3wp.exe), 該進程會建立一個應用程序域(一個進程能夠建立多個應用程序域, 一個應用程序域中能夠跑多個線程), 在應用程序域中會建立asp.net執行環境HttpRuntime, 在asp.net執行環境中建立HttpContext對象並啓動HttpApplication應用程序管道(HttpRuntime中能夠同時建立多個HttpContext和HttpApplication, 也就是說在HttpRuntime中能夠同時進行多套處理流程). 其中, HttpContext其實就是對一堆參數的封裝, 如: HttpRequest、HttpResponse、HttpServerUtility等, HttpContext能夠在整個HttpApplication管道中使用, 能夠取出其中的參數, 而且HttpContext自己也能夠看成一個字典來使用. HttpApplication也就是asp.net應用程序管道或者叫asp.net應用程序生命週期, 它是實際處理客戶端請求的地方, 咱們只能經過Global.asax或者IHttpModule接口來人爲控制客戶端請求的處理過程.緩存

 

在HttpApplication啓動後, 在IIS6.0中會有17個事件(IIS7.0中會更多), 其中包括: 用戶驗證、用戶受權等等, 這些事件中有兩個重要的時間點, 第一個時間點是在第7個事件以後, 也就是PostResolveRequestCache事件以後, 映射請求的處理程序(需查找配置文件); 第二個時間點是在第11個事件以後, 也就是PreRequestHandlerExecute事件以後, 執行處理程序, 像對*.aspx的處理程序System.Web.UI.PageHandlerFactory.Page就是實現了IHttpHandler接口的類. 在這個時間點中執行處理程序並進入頁面的生命週期, 其中最重要的是頁面處理的11個事件(如: Page_Int、Page_Load、Page_PreRender、Page_SaveState、Page_Unload), 咱們在這11個事件中, 完成讀取aspx頁面模板、控件預讀、讀取視圖狀態、控件屬性賦值等工做, 而且要求咱們程序員調用BLL、DAL、DE以及數據庫等完成業務邏輯的處理工做. 最後, 在由頁面對象中的Render方法將結果發送給客戶端瀏覽器, 客戶端瀏覽器再呈現給用戶.

至此, 完成了一次完整的請求過程. 那麼, 咱們的緩存工做應該在哪裏作呢?

 

緩存該在哪裏作, 取決於咱們能在整個請求過程的什麼位置動手. 咱們依然從客戶端瀏覽器開始:

首先, 最好的狀況是客戶端不發送任何請求直接就能得到數據, 這種狀況下, 用於緩存的數據保存在客戶端瀏覽器的緩存中.

 

其次, 在具備代理服務器的網絡環境中, 代理服務器能夠針對那些常常訪問的網頁製做緩存, 當局域網中第一臺主機請求了某個網頁並返回結果後, 局域網中的第二臺主機再請求同一個網頁, 這時代理服務器會直接返回上一次緩存的結果, 並不會向網絡中的IIS服務器發送請求, 例如: 如今的鏈接電信和網通線路的加速器等. 可是代理服務器一般有本身專門的管理軟件和管理系統, 做爲網站開發人員對代理服務器的控制能力有限.

 

再次, 前面也說過, 當用戶將請求地址發送到IIS服務器時, IIS服務器會根據請求地址選擇不一樣的行爲, 如: 對於*.aspx頁面會走應用程序管道, 而對*.html、*.jpg等資源會直接返回資源, 那麼咱們能夠把那些頻繁訪問的頁面作成*.html文件, 這樣用戶請求*.html, 將不用再走應用程序管道, 所以會提高效率. 例如: 網站首頁、或某些突發新聞或突發事件等, 能夠考慮作成靜態網頁. 就拿」天氣預報的發佈頁面」打比方: 天氣預報的發佈頁面的訪問用戶很是多, 咱們能夠考慮將發佈頁作成靜態的*.html網頁, 以後在整個網站程序啓動時, 在Gboabl.asax的Application_Start事件處理器中, 建立子線程以實現每3個小時從新獲取數據生成新的天氣發佈頁面內容.

 

以後的asp.net的處理流程, 做爲程序員咱們是沒法干涉的. 直到啓動HttpApplication管道後, 咱們才能夠經過Global.asax或IHttpModule來控制請求處理過程, 在應用程序管道中適合作整頁或用戶控件的緩存. 如: 緩存熱門頁面, 咱們能夠自動緩存整個網站中訪問量超過必定數值(閥值)的頁面, 其中爲了減少IO操做, 將緩存的頁面放在內容中.

 

最後, 咱們程序員能夠操做的地方就是頁面處理管道或者稱爲頁面生命週期, 咱們能夠頁面對象的11個事件中, 採用數據緩存的方式減少訪問數據庫的次數, 也就是減小IO操做. 還有些真假分頁等問題涉及網站的優化, 稍後再討論.

 

總之, 咱們能夠採用緩存的地方有如下5處:

1. 客戶端瀏覽器的緩存(非Cookie, Cookie只能放字符串信息)

2. 若是有代理服務器的話, 能夠在代理服務器中緩存整個網頁(本篇僅討論網站開發的相關內容)

3. 將頻繁訪問的資源作成*.html等的靜態內容, (咱們能夠將靜態資源緩存在ramdisk等開闢的內存盤中,  以進一步減小對硬盤的訪問).

4. 在應用程序處理管道或應用程序生命週期中, 進行整頁緩存或用戶控件的局部緩存

5. 在頁面處理管道或頁面生命週期中, 作數據緩存

 

下面咱們來詳細論述這些緩存手段:

1. 客戶端瀏覽器上的緩存(非Cookie, Cookie中的內容爲: 鍵和值均爲string類型的鍵值對)

咱們能夠經過在Http迴應中增長特定的頭部說明來指定瀏覽器的緩存策略; 添加頭部說明的手段既能夠經過頁面指令聲明設置, 也能夠經過編程方式設置.

 

對於圖片、Javascript腳本、CSS等資源, 能夠在IIS管理器中, 右擊圖片等資源, 選擇」屬性」 --> HttpHeaders後, 勾選Enable Content Expiration並設置時間便可. 一種值得推薦的手段是, 將須要緩存的資源分類, 如: image/dynamic/、image/static/, 這樣咱們能夠再文件夾上, 選擇屬性中的HttpHeaders進行設置. 不然, 針對每個靜態資源設置HttpHeaders將是件很是痛苦的事情. 此外, 還能夠採用一款名爲CacheRight的工具能夠提供緩存資源的統一配置.

 

查看或設置瀏覽器緩存位置:  IE --> Internet選項 --> 常規 --> 臨時Internet文件 --> 設置

 

Html文件的Head中的緩存設置:

<meta http-equiv="pragma" content="no-cache" />

<meta http-equiv="Cache-Control" content="no-cache" />

<meta http-equiv="expires" content="Wed, 26 Feb 1997 08:21:57 GMT" />

瀏覽器中關於Cache的3屬性:

Cache-Control:

設置相對過時時間, max-age指明以秒爲單位的緩存時間. 若對靜態資源只緩存一次, 能夠設置max-age的值爲315360000000 (一萬年).

Http協議的cache-control的常見取值及其組合釋義:

no-cache: 數據內容不能被緩存, 每次請求都從新訪問服務器, 如有max-age, 則緩存期間不訪問服務器.

no-store: 不只不能緩存, 連暫存也不能夠(即: 臨時文件夾中不能暫存該資源)

private(默認): 只能在瀏覽器中緩存, 只有在第一次請求的時候才訪問服務器, 如有max-age, 則緩存期間不訪問服務器.

public: 能夠被任何緩存區緩存, 如: 瀏覽器、服務器、代理服務器等

max-age: 相對過時時間, 即以秒爲單位的緩存時間.

no-cache, private: 打開新窗口時候從新訪問服務器, 若設置max-age, 則緩存期間不訪問服務器.

private, 正數的max-age: 後退時候不會訪問服務器

no-cache, 正數的max-age: 後退時會訪問服務器

點擊刷新: 不管如何都會訪問服務器.

Expires:

設置以分鐘爲單位的絕對過時時間, 優先級比Cache-Control低, 同時設置Expires和Cache-Control則後者生效.

Last-Modified:

該資源的最後修改時間, 在瀏覽器下一次請求資源時, 瀏覽器將先發送一個請求到服務器上, 並附上If-Unmodified-Since頭來講明瀏覽器所緩存資源的最後修改時間, 若是服務器發現沒有修改, 則直接返回304(Not Modified)迴應信息給瀏覽器(內容不多), 若是服務器對比時間發現修改了, 則照常返回所請求的資源.

 

注意:

Last-Modified屬性一般和Expires或Cache-Control屬性配合使用, 由於即便瀏覽器設置緩存, 當用戶點擊」刷新」按鈕時, 瀏覽器會忽略緩存繼續向服務器發送請求, 這時Last-Modified將可以很好的減少迴應開銷.

 

ETag將返回給瀏覽器一個資源ID, 若是有了新版本則正常發送並附上新ID, 不然返回304, 可是在服務器集羣狀況下, 每一個服務器將返回不一樣的ID, 所以不建議使用ETag.

 

以上描述的客戶端瀏覽器緩存是指存儲位置在客戶端瀏覽器, 可是對客戶端瀏覽器緩存的實際設置工做是在服務器上的資源中完成的. 雖然剛纔咱們介紹了有關於客戶端瀏覽器緩存的屬性, 可是實際上對這些屬性的設置工做都須要在服務器的資源中作設置. 咱們有兩種操做手段對瀏覽器緩存進行設置, 一個是經過頁面指令聲明來設置, 另一個是經過編程方式來設置.

 

瀏覽器緩存的設置手段:

第一: 經過頁面指令聲明來設置HTTP的緩存

頁面指令<%@ OutputCache Location=」Any」 Duration=」10」 VaryByParam=」ProductId」 VaryByHeader=」Accept-Language」%>中的Location用來設置緩存的位置, 該屬性常見的值爲:

Any(默認): 輸出緩存能夠位於任何地點, 對應於HttpCacheability.Public. 如: 客戶端瀏覽器、代理服務器或服務器自己.

Client: 只能位於發出請求的客戶端瀏覽器, 對應於HttpCacheability.Private.

Downstream: 輸出緩存能夠位於除服務器自己的其餘任何地方, 如: 客戶端瀏覽器、代理服務器.

Server: 輸出緩存位於Web服務器自己, 對應於HttpCacheability.Server

ServerAndClient: 輸出緩存只能位於服務器自己或客戶端瀏覽器, 對應於HttpCacheability.Private和HttpCacheability.Server

None: 禁用輸出緩存, 對應於HttpCacheability.NoCache.

VaryByParam屬性: 根據請求參數的不一樣而緩存不一樣的版本. 多個值用分號(;)分隔, *號表示爲任意參數或參數組合緩存不一樣版本, 「none」表示只緩存一個版本.

VaryByHeader屬性: 根據請求頭來緩存不一樣的版本, 如同一頁面的不一樣語言版本.

VaryByCustom屬性: 根據自定義參數來緩存不一樣的版本, 如: VaryByCunstom=」browser」是系統已實現的, 根據瀏覽器名稱和版本號緩存不一樣的版本. 也能夠, 根據自定義參數來緩存, 如: VaryByCustom=」happy」, 此時系統不知道如何解釋happy, 所以須要在Global.asax或IHttpModule實現類中重寫GetVaryByCustomString()方法, 來完成處理邏輯.

VaryByControl屬性: 根據用戶控件中的服務器控件ID來緩存不一樣版本.

更高級的方式, 是經過配置文件來設置HTTP的緩存.

頁面指令爲<%@ OutputCache CacheProfile=」cacheconfig」%>, 其中cacheconfig是配置文件中的緩存配置節中CacheProfile的名稱.

View Code

 

第二: 經過編程方式設置HTTP的緩存特性

在頁面後臺文件*.cs文件或*.ashx等其餘代碼文件中, 咱們能夠經過HttpResponse類的實例對象上的Cache屬性or頁面對象的Response屬性上的Cache屬性來設定緩存特性.

VaryByHeaders[「Accept-Language」] = true: 設置緩存同一頁的不一樣語言版本

SetMaxAge(): 設置活動過時時間

SetExpires()方法: 設置絕對過時時間

SetLastModified(): 設置最後修改時間

SetNoStore(): 設置不要緩存

SetNoServerCaching(): 關閉服務器緩存

SetCacheability(): 設置緩存位置, 其參數類型爲HttpCacheability枚舉類型, 可選項以下:

  NoCache: 將會在Http響應頭添加Cache-Control: no-cache標頭, 表示禁用緩存.

  Private: 只在客戶端瀏覽器緩存(關閉代理服務器緩存).

  Public: 可在任意位置緩存.

  Server: 只能在服務器上緩存.

  ServerAndNoCache: 只能在服務器上緩存, 其他都不能緩存, 將會在Http響應頭添加Cache-Control: no-cache標頭.

  ServerAndPrivate: 只能在服務器和客戶端瀏覽器緩存, 不能再代理服務器上緩存.

 

注意:

其中, 設置NoCache和ServerAndNoCache, 將會在Http響應頭中添加以下內容:

        Cache-Control: no-cache

              Pragma: no-cache

              Expires: -1

以上設置可防止瀏覽器在本身的歷史記錄文件夾中緩存該頁, 前進和後退按鈕都會請求響應新的版本.

 

SetAllowResponseInBrowserHistory()方法: 容許在瀏覽器歷史記錄中響應, 若參數爲true, 則可覆蓋HttpCacheability的設置, 但若是SetCacheability設置爲NoCache或ServerAndNoCache, 則會忽略SetAlloewResponseInBrowerHistory的值.

 

Cookies和代理緩存不能結合的很好, 當使用cookie時,不要容許代理緩存。設置Location爲Private、Server或ServerAndPrivate。

 

用戶控件中的緩存(部分頁緩存)不可使用Location屬性, 由於在不一樣頁面中, 同一用戶控件緩存的結果將會有多個. 若是但願用戶控件緩存同一結果, 能夠添加Shared=」true」屬性來實現.

 

頁面緩存和用戶控件緩存的Duration屬性的時間長短問題: 結論是按照時間長的來算. 如: 頁面緩存長, 用戶控件緩存短, 則用戶控件的緩存內容會和頁面緩存一塊兒到期; 若用戶控件緩存長, 頁面緩存短, 則用戶控件的緩存內容會一直到用戶控件的緩存時間結束.

 

Http Referer是Request.Header的一部分, 他能夠指出是從哪一個頁面連接過來的, 可是能夠僞造. 咱們能夠經過this.Request.Headers[「Referer」]或this.Request.UrlReferrer得到其內容, 最好經過this.Request.UrlReferrer.OriginalString來避軌編碼不一致的問題.

 

2. 和代理服務器相關的緩存措施

對咱們程序員來講, 和代理服務器相關的操做無非也是在服務器上設置的, 參見: 1中的瀏覽器緩存設置中的操做手段.

 

3. 將頻繁訪問的資源作成*.html等的靜態內容

像天氣預報這樣的即時性要求不是很強的頁面, 很是適合作成靜態的html頁面. 咱們能夠在網站的Global.asax中, 每3個小時從新讀取一下數據, 將讀取的數據生成新的html頁面並覆蓋掉老頁面. 網站首頁也是訪問極其頻繁的, 作法相似, 只是時間短點. 爲簡便起見, 直接用txt文件模擬數據庫.

//App_Data/WeatherData.txt, 內容隨便.

// WeatherReport/Today.html

View Code

 

注意: Global.asax是繼承自HttpApplication類的子類, 若是網站存在Global.asax, 它會在HttpRuntime運行環境建立HttpApplication類的對象時, 用Global類的對象替帶HttpApplication類的對象, 而且Application_Start、Application_End事件激發只會在服務器啓動時進行, 而Application_BeginRequest等事件處理器則每次請求時都會激發. 本例在Application_Start中爲保證主線程不被過度佔用, 採用子線程建立timer並更新天氣信息.

//Global.asax

View Code

 

4. 經過Global.asax或IHttpModule在應用程序處理管道中作整頁緩存或用戶控件局部緩存

第3所述的狀況, 能夠認爲是在IIS級別的緩存, 它的目的是避免或勁量少走應用程序處理流程. 可是這種IIS級別的緩存只適合那些不多的熱門資源, 若是全部頁面都作成靜態頁面就又回到了古老的純html時代, 固然是得不償失. 那麼大部分的資源還須要是動態的, 也就是說必需要走應用程序處理流程, 程序員能夠經過Global.asax和IHttpModule來人爲的影響應用程序處理管道. 所以, 在HttpApplication級的緩存是正是經過Global.asax或IHttpModule來完成的.

 

「動態頁面靜態化」:

在文章的標題中, 提到過一種稱爲」動態頁面靜態化」的技術, 暫且無論這個稱謂是否貼切(園子有議論這個的帖子). 不管名稱是什麼, 我以爲那只是個稱謂而已, 關鍵的是它用來解決什麼問題. 早期的搜索引擎並不能很好的收錄如*.aspx等的動態頁面, 可是對*.html等靜態頁面收錄良好, 因而乎產生了一種UrlRewrite(Url重寫)的技術. 另一種」動態頁面靜態化」的技術就和今天的緩存掛鉤了, 那就是把用戶對*.aspx頁面的請求結果緩存到html文件中.

 

第一種: UrlRewrite技術:

它的原理是把用戶發出的對靜態頁面(*.html)的請求轉化映射爲對動態頁面(*.aspx)的請求. UrlRewrite技術主要就是用來解決當時搜索引擎收錄的問題, 現在的各大搜索引擎中宣稱已經不存在這個問題了. 那麼這個技術現今就沒用了嗎? 在一篇講Yahoo團隊的34條網站優化手段的文章中, 看到一處用UrlWriter的地方, 說的是爲了減少客戶端瀏覽器在請求迴應過程當中傳遞的數據, 在網站真正實施發佈時儘可能採用」短名稱」, 如: 超連接中用p.html代替product.html、在JS中用d表明window.document等. 這篇文章說短文件名有利於減小傳輸過程當中的數據量, 可是客戶體驗較差, 這時就能夠經過UrlRewrite技術來改善用戶體驗, 具體作法是在html代碼中使用p.html作超連接, 用戶請求時依然採用product.html, UrlRewrite的工做就是把請求中的product.html轉換映射爲p.html. 這和」把*.html映射到*.aspx」是一個道理. 再有一個和緩存相關的用處, 就是url中含有相似*.aspx?id=123的查詢字符串時, 不少代理是不會緩存這種請求的, 這時咱們能夠經過UrlRewrite技術將*.aspx?id=123變成/123.aspx, 從而使代理服務器緩存這種請求.

示例: 靜態*html映射到動態頁面*.aspx, 由於默認對*.html文件的處理是由IIS來完成的, 所以這裏必須在IIS中, Web程序的屬性中, 「主目錄」 ---> 「應用程序設置」 --> 「配置」中分別添加*.htm和*.html到aspnet_isapi.dll的配置. 注意: 「檢查文件是否存在」 的勾必定要去掉, 這時對*.htm和*.html的處理將會交給asp.net處理.

// UrlRewriteDemo/App_Code/ HtmlToAspx.cs

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace HtmlAspxModel
{
/// <summary>
/// Summary description for HtmlToAspx
/// </summary>
public class HtmlToAspx : IHttpModule
{
#region IHttpModule Members

public void Dispose()
{
throw new NotImplementedException();
}

public void Init(HttpApplication application)
{
//更通用的作法是採用IHttpModule來操做HttpApplication管道
//註冊事件
application.BeginRequest += new EventHandler(application_BeginRequest);
}

void application_BeginRequest(object sender, EventArgs e)
{
HttpApplication ha = sender as HttpApplication;
HttpContext hc = ha.Context;

string rawurl = ha.Context.Request.RawUrl;

if (rawurl.EndsWith(".htm",StringComparison.OrdinalIgnoreCase)) //判斷以*.htm結尾
{
string regnoparam = @"/(\w+)\.htm"; //不含參數
string regparam = @"/(\w+)\=(\d+)"; //含參數
if (System.Text.RegularExpressions.Regex.Match(rawurl, regparam, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) //匹配成功
{
System.Text.RegularExpressions.MatchCollection mc = System.Text.RegularExpressions.Regex.Matches(rawurl, regparam, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
rawurl = rawurl.Replace(".htm", "");
for (int i = 0; i < mc.Count; i++)
{
if (i == 0)
{
rawurl = rawurl.Replace(mc[i].Value, ".aspx?"+mc[i].Value.Substring(1,mc[i].Value.Length-1));
}
else
{
rawurl = rawurl.Replace(mc[i].Value, "&" + mc[i].Value.Substring(1, mc[i].Value.Length - 1));
}
}
}
else if (System.Text.RegularExpressions.Regex.Match(rawurl, regnoparam, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success)
{
rawurl = rawurl.Replace(".htm", ".aspx");
}
hc.RewritePath(rawurl);
}
}

#endregion
}
}
複製代碼

 

//UrlRewriteDemo/Default.aspx

View Code

 

//UrlRewriteDemo/WebFormNoParam.aspx

View Code

 

//UrlRewriteDemo/WebFormWithParam.aspx

View Code

 

//UrlRewriteDemo/web.config

View Code

 

補充知識 --- 重置Form表單的Action屬性:

a. 自定義類RawHtmlForm繼承自HtmlForm控件

b. 重寫RenderAttribute()方法

View Code

 

c. 在配置文件中, 指明用新控件RawHtmlForm替代HtmlForm控件, 這樣在生成form標籤時, 自動使用新控件代替舊控件.

View Code

 

第二種: 緩存爲Html文件的技術(*.aspx頁面的請求結果就是個html頁面):

它的原理是客戶端請求*.aspx文件, 若是在緩存文件夾中有對應名稱的*.html文件, 則將請求地址替換爲*.html的文件. 這樣就沒必要走徹底部的應用程序處理管道, 響應速度會大大提升; 若是緩存文件夾中沒有對應的*.html文件, 則該請求會走完整個應用程序管道, 在應用程序管道中的頁面處理部分將*.aspx頁面的請求結果返回給客戶端同時, 再以流的形式保存(另存)*.aspx請求結果到緩存文件中. 這樣作的好處是加快網站的響應速度, 而一個快速響應的網站對提高SEO(Searching Engine Optimization)效果也是有必定幫助的. 這種手法是用對文件的IO操做代替費時的應用程序管道的操做, 對於那些訪問量極高的頁面, 咱們甚至能夠把*.aspx頁面的請求結果直接保存到內存中, 這樣連IO操做也省掉了, 但內存的有限性決定了咱們不可以保存太多的頁面在內存中, 只能保存那些訪問量極高的少許頁面.

 

緩存後替換:

以上咱們實現了整頁的緩存, 可是緩存完的數據是關於整個頁面的, 是沒辦法修改其內容的. 仍然拿新聞頁面來打比方: 若是我想在每一個新聞頁面中顯示下當前日期和時間, 或者我想在每一個新聞頁面的頭部和尾部添加廣告, 針對這種大部份內容不變僅有少部份內容發生變化的狀況, ASP.NET提供了緩存後替換功能.

緩存後替換功能能夠將整個頁面進行輸出緩存, 可是特定的部分標記爲不緩存. Substitution控件指定須要動態建立而不進行緩存的部分, 相似於佔位符, 動態生成的內容將輸出到Substitution控件所在的位置. ASP.NET爲咱們提供了3種方式的緩存後替換: a. 以聲明的方式使用Substitution控件 b. 以編程的方式使用Substitution控件 c. 使用AdRotator控件.

 

a. 以聲明的方式使用Substitution控件時: Substitution控件調用MethodName屬性指定的方法, 該方法提供了在Substitution控件處顯示的內容, 而且該方法必須返回字符串類型的結果, 並且必須在Page或UserControl類的代碼中包含該控件指定的這個靜態方法. MethodName指定的方法並需符合HttpResponseSubstitutionCallback委託的約定(以參數爲HttpContext類型), 簡言之就是MethodName指的方法, 必須是static、返回string、參數爲HttpContext類型的方法.

 

b. 以編程的方式使用Substitution控件時: 能夠將當前頁或用戶控件後臺代碼中的靜態方法 或 任何對象上的靜態方法的方法名, 傳遞給HttpResponse.WriteSubstitution()方法. 第一次請求該頁的時候, WriteSubstitution方法調用HttpResponseSubstitutionCallback委託來產生輸出, 以後將替換緩存區中的內容. 該方法會將客戶端的緩存能力從public將爲server, 從而使瀏覽器不在緩存而保證可以從新生成靜態內容.

 

c. AdRotator服務器控件: 該服務器控件是在其內部支持緩存後替代功能. 若是將AdRotator控件放在頁面上, 則每次請求時都會生成動態的廣告. 所以, 包含AdRotator控件的頁面僅能在服務器端緩存, 能夠經過AdRotator的AdCreated事件對廣告內容做高級的處理. 經過AdRotator的AdvertisementFile屬性指定儲存廣告數據的文件, 一般是xml文件. 配置廣告信息的xml的格式, 見示例文件XmlAdData.xml.

AdRotator的幾個屬性釋義以下:

ImageUrl: 廣告圖片地址

NavigateUrl: 點擊廣告圖片的連接地址

AlternateText: 鼠標放在圖片上的提示文字

Keyword: 該廣告的關鍵詞

Impression: 權重, 廣告的顯示頻率.

示例: 對於新聞類型的網站, 站內的大量文章內容發生改變的頻率不是很高, 所以適合將這些文章內容緩存爲靜態的html頁面, 以減小文章內容頁重複生成過程的開銷.

//CacheHtmlPageDemo/App_code/ CacheHtmlModule.cs    ---   若在CacheFile文件夾中, 存在緩存的*.html則返回客戶端

View Code

 

//CacheHtmlPageDemo/App_code/HtmlPageBase.cs --- 若不存在緩存的*.html, 則走頁面處理管道並將生成的頁面保存到CacheFile文件夾中, 咱們所要作的只是將頁面後臺的.cs文件中的類繼承成自HtmlPageBase類而非原先的Page類.

View Code

 

//CacheHtmlPageDemo/Default.aspx

View Code

 

//CacheHtmlPageDemo/News1.aspx

View Code

 

//CacheHtmlPageDemo/News3.aspx , 緩存後替換示例, 該頁面添加了@OutputCache頁面指令, 且後臺代碼繼承自默認的Page類.

View Code

 

//CacheHtmlPageDemo/App_Data/XmlAdData.xml, 爲簡便而使用xml, 若用數據庫也能夠, 將XML節點對應表的字段(隨便兩張圖片就行).

View Code

 

// CacheHtmlPageDemo/web.config

View Code

 

使用緩存時, 關於緩存內容的考量:

a. 沒有必要緩存用戶相關頁面, 這會存儲不少低點擊率的頁面

b. 緩存那些頻繁請求, 可是內容更新不太頻繁或者提供過時內容也影響不大的頁面.

c. 生成操做很昂貴的頁面, 如: 電子商務網站中的, 分類顯示和報表顯示頁面的生成操做.

d. 內存空間有限, 因此只能緩存那些佔用空間小的內容.

e. 對於Postback的響應不該該被緩存, 由於他們依賴於post請求參數.

5. 在頁面處理管道或經過IHttpHandler在處理程序中作數據緩存.

 

提及數據緩存, 確定跟網站應用程序的數據來源有關了, 須要被緩存的那部分數據固然是放在Cache中. 那麼數據自己要麼是存放在文件中, 要麼是存放在數據庫中, 所以對應於這兩種數據載體, .net提供了2種依賴機制: 一種是基於文件和目錄的CacheDependency, 一種是基於數據庫的SqlDependency, SqlDependency是數據庫和程序之間的依賴關係, 此外微軟還提供了繼承自CacheDependency的SqlCacheDependency類, 用來創建數據庫和程序緩存之間的依賴關係. 這種緩存依賴解決個什麼問題呢, 數據不是已經緩存了嗎? 沒錯, 咱們是緩存了數據, 可是若是原始數據發生變化怎麼辦? 緩存依賴就是當數據發生變化時, 自動清除緩存中數據的這麼一種機制, 接下來的下一次請求必然要重新執行讀取數據的操做.

緩存依賴執行的操做是刪除緩存內容, 這樣下一次請求勢必會重新獲取數據. 這與」緩存後替換」功能是不同的, 緩存後替換功能是用讀取的新數據替換緩存頁面的一部份內容.

 

第一種: 基於文件和目錄的CacheDependency是比較簡單的, 留意緩存依賴的過時時間、緩存項的優先級、以及刪除通知(當被緩存的數據從緩存中移除時激發的事件)便可.

 

值得注意的地方: 刪除通知的回調方法(委託)的回調時機是不可預知的, 頗有可能在回調的時候, 頁面的生成過程已經完成, 這時候根本沒有HttpContext對象, 因此對緩存的操做只能經過HttpRuntime的Cache屬性來得到. 並且, 不能在頁面上使用實例方法來做爲回調方法, 由於回調方法會阻止頁面對象的垃圾回收, 內存資源將很快消耗光.

//DataCacheDemo/App_Code/ FileCacheManager.cs

View Code

 

//DataCacheDemo/App_Data/TextFileData.txt, 文字內容隨意

//DataCacheDemo/CacheDependency.aspx

View Code

 

//DataCacheDemo/CacheDependency.aspx.cs

View Code

 

第二種: 基於Sql的緩存依賴有兩種實現: a. 基於輪詢的實現 和 b. 基於通知的實現(僅支持Ms Sql 2005 以上的版本).

 

  1. 基於輪詢的實現: 就是由 應用程序 每隔必定時間去訪問數據庫, 查看數據是否發生變化.

注意: 輪詢的實際操做, 不可能每次都去查詢整個數據庫, 輪詢機制其實是經過觸發器來維護一張監控表來實現的.

 

具體操做以下:

建立一張監控信息表, 表的字段主要有兩個(被監控的表名, int型字段用來表示數據是否發生變化).

在被監控表上創建觸發器, 當表的內容發生變化時, 修改監控表的int字段, 能夠經過表的Insert、Delete、Update觸發器實現.

對於除Sql Server之外的數據庫, 如Oracle、MySql等可採用相似的辦法創建輪詢機制或通知機制(固然Sql Server也能夠這麼作). 而Sql Server數據庫已經由微軟提供了一套輪詢實現機制.

(1) . 工具位置: c:\Windows\Microsoft.Net\Framework\v2.0.50727\aspnet_regsql.exe

該工具的參數信息以下(區分大小寫):

-S 數據庫服務器的名稱或ip地址

-E 使用集成驗證方式登陸數據庫

-U 用戶名

-P 密碼

-d 數據庫名稱, 如不提供則使用aspnetdb數據庫

-ed 爲數據庫打開Sql緩存依賴支持

-dd 關閉數據庫的Sql緩存依賴支持

-et 指定Sql緩存依賴使用的表, 須要使用-t指定表名

-dt 禁用Sql緩存依賴使用的表, 須要使用-t指定表名

-t 指定表名

-lt 列出啓用緩存依賴的表名

示例:

啓用數據庫緩存依賴: aspnet_regsql –S .\MSSQLServer –E –d northwind –ed

啓動對被監控表的監控: aspnet_regsql –S .\MSSQLServer –E –d northwind –t calendar –et

列出啓用了緩存依賴的表: aspnet_regsql –S .\MSSQLServer –E –d northwind –lt

 

(2). 運用aspnet_regsql工具配置完數據庫後, 還須要在配置文件中增長輪詢的設置. sqlCacheDependency enable屬性爲」true」表示開啓掄起機制, polltime設置以毫秒爲單位的輪詢間隔(>= 500ms, 默認爲1分鐘).

View Code

 

SqlCacheDependency表示Sql緩存依賴對象, 即支持輪詢機制也支持通知機制. SqlCacheDependency scd = new SqlCacheDependency(「配置文件中的數據庫那一項的名稱, 如northwindCache」, 「被監控的表名」);

 

當構造緩存依賴對象時, 能夠經過AggregateCacheDependency建立多個依賴的組合, 在該組合中任一依賴發生變化時將使緩存失效. 組合的緩存依賴內部爲一個集合, 例如: 緩存的數據是依賴於Customers和Orders兩張表, 咱們能夠創建2個緩存依賴對象, 並將其添加到一個AggregateCacheDependency對象中, 這時只要任何緩存依賴發生變化, 緩存數據將會失效.

//涉及Sql的相關操做, MsSqlCommand.txt

View Code

 

 

//DataCacheDemo/App_Code/IDALFactory.cs

View Code

 

//DataCacheDemo/App_Code/SqlDAL.cs

View Code

 

//DataCacheDemo/App_Code/OracleDAL.cs

View Code

 

//DataCacheDemo/App_Code/pollingSqlDependency.aspx

View Code

 

//DataCacheDemo/App_Code/pollingSqlDependency.aspx.cs

View Code

 

//DataCacheDemo/App_Code/web.config

View Code

 

基於Oracle實現輪詢操做, 關鍵點就是對監控表的操做及觸發器的編寫. 這部分代碼我沒有寫, 但它和Oracle通知機制的緩存依賴很像(見基於通知的緩存依賴Oracle示例). 我說個思路: 首先還是建立監控表, 以後在被監控表上建立觸發器, 對該表作的Insert、Update、Delete操做都會修改監控表(在個人基於通知的緩存依賴Oracle示例中, 我製做了個函數完成添加監控表記錄、添加被監控表觸發器、以及建立本地文件的操做). 最後, 咱們能夠再Galobal.asax的Application_Start事件中開闢子線程, 在子線程中讀取配置文件中的事件設置, 按期去讀取監控表的數據.

 

再具體點就是, 咱們應該事先緩存監控表, 在子線程讀取監控表數據後, 比較兩個DataTable的內容, 找出發生變化的表的名字. 而後利用索引器查看緩存中是否有以這個表名做key的緩存項, 若是有的話清空這個緩存項, 沒有則什麼也不作. 大致思路是這樣, 代碼能夠參考Orale的通知機制的實現, 這篇已經寫了好久了, 不想再寫了.

 

2. 基於通知的實現: 當數據發生變化時, 由數據庫直接通知應用程序. 雖然, 通知機制不須要使用工具作數據庫準備工做, 也不須要在配置文件中作任何修改, 可是通知機制只能應用在Ms Sql Server 2005以上的版本中.

 

具體操做:

使用命令啓動數據庫通知機制(默認是開啓狀態, 請務必先檢查): alter database 數據庫名稱 set enable_broker

在Global.asax文件中的Application_Start和Application_End事件中分別設置開啓和結束SqlDependency的監聽.

經過一個SqlCommand對象來獲取數據庫查詢通知, 該SqlCommand對象必須知足條件, 第一是數據庫的表名必須是徹底限定名(如: dbo.Region), 第二是Select語句必須顯示執行列名, 不能用*、top函數等, 即只能是最簡單的Select語句.

構造緩存依賴對象, SqlCacheDependency scd = new SqlCacheDependency(command);

示例:

//涉及Sql的相關操做, OracleCommand.txt.txt

View Code

 

//DataCacheDemo/Global.asax

View Code

 

//DataCacheDemo/App_Code/noticeSqlDependency.aspx.aspx

View Code

 

//DataCacheDemo/App_Code/noticeSqlDependency.aspx.aspx.cs

View Code

 

差點忘了, 還有網站優化:

一般咱們面對一個站時, 能夠從如下幾個方面考慮優化的問題:

a. Ajax, 使用Ajax技術提高客戶體驗

b. 瀏覽器緩存(客戶端緩存, 緩存後替換)

c. 應用程序處理管道級別的緩存(整頁級緩存, 含IIS位置的整頁緩存)

d. 控件級緩存(用戶控件中使用緩存技術)

e. 數據集緩存(效果較明顯)

f. 代碼級的優化

相關文章
相關標籤/搜索