ASP.NET 缓存技术(一)——启用页面输出缓存
作者寄語(yǔ):MSDN 是最好的老師,互聯(lián)網(wǎng)是最智慧的生命體,分享是最重要的成長(zhǎng)途徑,技術(shù)的進(jìn)步在于學(xué)習(xí)、實(shí)踐和創(chuàng)新!
????? 本系列所講述的技術(shù)和展示的代碼適用于 .NET Framework 4.0 和 IIS7 下的 ASP.NET 4.0,所附示例代碼使用 Visual Studio 2010 開(kāi)發(fā),并可能需要使用 SQL Express 服務(wù)。作者在學(xué)習(xí) MSDN 和參考網(wǎng)絡(luò)文獻(xiàn)的基礎(chǔ)上對(duì)相關(guān)技術(shù)進(jìn)行了歸納整理,如有不足和錯(cuò)誤,歡迎大家指正。
為什么使用緩存技術(shù)?
????? 這可能是一個(gè)很好回答的問(wèn)題,那就是為了提升應(yīng)用程序的性能。但更多的人并不十分清楚應(yīng)該在什么時(shí)候,怎樣去應(yīng)用緩存技術(shù)。通常來(lái)說(shuō),一般的 ASP.NET 應(yīng)用程序很少有人去關(guān)注緩存的使用,但是當(dāng)這樣的應(yīng)用程序因?yàn)樾枨蟮臄U(kuò)大,需要處理更多的數(shù)據(jù),需要承載更多的用戶,需要采用更復(fù)雜的部署時(shí),普通的方法絕對(duì)會(huì)讓你對(duì)系統(tǒng)的性能表現(xiàn)感到束手無(wú)策。在現(xiàn)在內(nèi)存并不昂貴的時(shí)代,良好的緩存應(yīng)用將會(huì)比優(yōu)化程序代碼為系統(tǒng)帶來(lái)的性能提升更為廉價(jià)。在這里提到了良好的緩存應(yīng)用這種說(shuō)法,因?yàn)榫彺婕夹g(shù)并不是萬(wàn)能的,如何使用取決于系統(tǒng)的各種需求和指標(biāo)。
ASP.NET 緩存技術(shù)中的兩個(gè)層面
????? 在 ASP.NET 的應(yīng)用程序中,可以在頁(yè)面輸出和應(yīng)用程序數(shù)據(jù)兩個(gè)層面應(yīng)用緩存技術(shù)。
????? 對(duì)頁(yè)面輸出進(jìn)行緩存是指在內(nèi)存中保存處理后的 ASP.NET 頁(yè)面。通常對(duì) ASP.NET 頁(yè)面的請(qǐng)求都會(huì)導(dǎo)致 IIS7 經(jīng)歷如下過(guò)程:
??????
????? 當(dāng)對(duì)頁(yè)面輸出進(jìn)行緩存后,如果當(dāng)前請(qǐng)求的頁(yè)面的服務(wù)器緩存已經(jīng)存在,則以上的執(zhí)行過(guò)程在“解析緩存”這一步完成后就會(huì)停止并返回緩存的頁(yè)面內(nèi)容,由此也導(dǎo)致了頁(yè)面的相關(guān)事件不會(huì)得到執(zhí)行,直到該頁(yè)面的服務(wù)器緩存失效。ASP.NET 為頁(yè)面輸出緩存提供了兩種服務(wù)器緩存模型,整頁(yè)緩存和部分頁(yè)緩存,而部分頁(yè)緩存又可采用兩種工作方式:控件緩存和緩存后替換。更為出色的是,ASP.NET 還可以根據(jù)請(qǐng)求參數(shù)的不同而創(chuàng)建同一頁(yè)面的不同緩存。更多關(guān)于頁(yè)面輸出的緩存將在后文詳細(xì)講述。
????? 對(duì)應(yīng)用程序數(shù)據(jù)進(jìn)行緩存是通過(guò) ASP.NET 提供的一種編程方式來(lái)訪問(wèn)可以是任意數(shù)據(jù)的鍵/值對(duì),這些鍵/值對(duì)存儲(chǔ)在內(nèi)存中,使用方式與應(yīng)用程序狀態(tài)(System.Web.HttpApplicationState)類似,只是它們是易失的。更多關(guān)于應(yīng)用程序數(shù)據(jù)的緩存將在后文詳細(xì)講述。
開(kāi)啟頁(yè)面輸出緩存
????? 通過(guò)在 ASPX 頁(yè)面文件中包含 @ OutputCache 指令,并設(shè)置它的 Duration 和 VaryByParam 屬性就可非常方便的啟用頁(yè)面輸出緩存。Duration 屬性用于指定頁(yè)面輸出緩存的緩存時(shí)間,以秒為單位;VaryByParam 屬性這里先設(shè)置為 none,有關(guān)它的更多信息將在后文講述。以下代碼演示了如何將頁(yè)面輸出內(nèi)容緩存 60 秒:
C# CODE?? :No Title Code| 1234 56789 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <%@ Page Language="C#" AutoEventWireup="true" %> <%@ OutputCache Duration="60" VaryByParam="none" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"><title></title> </head> <body><script runat="server">protected void Page_Load(object sender, EventArgs e){lblNowTime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");}</script><form id="form1" runat="server"><div><asp:Label ID="lblNowTime" runat="server" /></div></form> </body> </html> |
????? 到這里,我們就已經(jīng)開(kāi)啟了頁(yè)面輸出緩存,該段代碼中的 Page_Load 方法在每次執(zhí)行后,至少會(huì)過(guò) 60 秒才會(huì)再次執(zhí)行(為什么用了“至少”這個(gè)詞,結(jié)合后面的講述想想吧)。
頁(yè)面輸出緩存的本質(zhì)
????? 在繼續(xù)深入之前,我們要先問(wèn)問(wèn),頁(yè)面輸出緩存是怎樣實(shí)現(xiàn)的呢?頁(yè)面輸出緩存的定義可以這樣來(lái)說(shuō),在頁(yè)面的響應(yīng)生命周期之內(nèi),將頁(yè)面的內(nèi)容存儲(chǔ)在可緩存設(shè)備上,在頁(yè)面過(guò)期之前,使用該存儲(chǔ)的內(nèi)容提供給用戶。可緩存設(shè)備包括發(fā)出請(qǐng)求的瀏覽器、響應(yīng)請(qǐng)求的 Web 服務(wù)器以及在請(qǐng)求和響應(yīng)流中其它任何具有緩存功能的設(shè)備,如代理服務(wù)器。在最基本的層面,HTTP 1.0 協(xié)議就已經(jīng)定義了簡(jiǎn)單的緩存機(jī)制,現(xiàn)在流行的最新瀏覽器所支持的 HTTP 1.1 協(xié)議更是在這個(gè)基礎(chǔ)上進(jìn)行了擴(kuò)展并消除了 HTTP 1.0 協(xié)議的缺陷。
????? HTTP 協(xié)議通過(guò)在頭部添加 Cache-Control 標(biāo)頭來(lái)控制頁(yè)面的緩存動(dòng)作。HTTP 1.1 協(xié)議中的 Cache-Control 標(biāo)頭有以下設(shè)置:
| 參數(shù) | 說(shuō)明 |
| public | 數(shù)據(jù)內(nèi)容皆被儲(chǔ)存起來(lái),就連有密碼保護(hù)的網(wǎng)頁(yè)也儲(chǔ)存,安全性很低。 |
| private | 數(shù)據(jù)內(nèi)容只能被儲(chǔ)存到私有的緩存中,通常是瀏覽器的本地緩存。這是 Cache-Control 標(biāo)頭的默認(rèn)值。 |
| no-cache | 數(shù)據(jù)內(nèi)容永遠(yuǎn)不會(huì)被儲(chǔ)存,代理服務(wù)器和瀏覽器讀到此標(biāo)頭就不會(huì)將數(shù)據(jù)內(nèi)容存入緩存。 |
| no-store | 數(shù)據(jù)內(nèi)容除不存入緩存中,也不能存入暫時(shí)的磁盤(pán)中,這個(gè)標(biāo)頭防止敏感性數(shù)據(jù)被復(fù)制。 |
| must-revalidate | 用戶在每次讀取數(shù)據(jù)時(shí),會(huì)再次和原來(lái)的服務(wù)器確定是否為最新數(shù)據(jù),而不是和中間的代理服務(wù)器。 |
| proxy-revalidate | 這個(gè)參數(shù)很像 must-revalidate,不過(guò)中間接收的代理服務(wù)器可以互相分享緩存。 |
| max-age=xxx | 數(shù)據(jù)內(nèi)容在 xxx 秒后失效,這個(gè)標(biāo)頭就像 Exires 標(biāo)頭(HTTP 1.0 協(xié)議定義的緩存過(guò)期機(jī)制)的功能一樣,不過(guò) max-age=xxx 只能服務(wù)于支持 HTTP 1.1 協(xié)議的瀏覽器。假設(shè)兩者都有,max-age 優(yōu)先。 |
????? 通過(guò) HTTP 協(xié)議控制頁(yè)面的緩存動(dòng)作,通常可以像如下示例一樣添加 Cache-Control 標(biāo)頭在響應(yīng)流的頭部:
| HTTP 標(biāo)頭 | 說(shuō)明 |
| Cache-Control:max-age=3600 | 指示頁(yè)面應(yīng)在 60 分鐘后過(guò)期,內(nèi)容不應(yīng)在代理服務(wù)器上緩存。 |
| Cache-Control:no-cache | 指示頁(yè)面應(yīng)每次向原來(lái)的服務(wù)器請(qǐng)求并獲取內(nèi)容。 |
| Cache-Control:public,max-age=3600 | 指示頁(yè)面應(yīng)在 60 分鐘后過(guò)期,內(nèi)容可以在瀏覽器的本地區(qū)域和代理服務(wù)器上緩存。 |
????? 但是通過(guò) Cache-Control 標(biāo)頭的控制作用還取決于用戶不同的瀏覽方式:
????? (1)打開(kāi)新窗口:如果 Cache-Control 的值為 private、no-cache、must-revalidate,那么打開(kāi)新窗口訪問(wèn)時(shí)都會(huì)重新訪問(wèn)服務(wù)器。而如果指定了 max-age=xxx 值,則在 xxx 秒時(shí)間內(nèi)都不會(huì)重新訪問(wèn)服務(wù)器。
????? (2)地址欄回車:如果 Cache-Control 的值為 private 或 must-revalidate,則只有第一次訪問(wèn)時(shí)會(huì)訪問(wèn)服務(wù)器,以后就不再訪問(wèn)。如果值為 no-cache,那么每次都會(huì)訪問(wèn)服務(wù)器。如果指定了 max-age,則在過(guò)期之前不會(huì)訪問(wèn)服務(wù)器。
????? (3)按后退按鈕:如果 Cache-Control 的值為 private、must-revalidate、max-age,則不會(huì)重新訪問(wèn)服務(wù)器。如果值為 no-cache,則每次都重新訪問(wèn)服務(wù)器。
????? (4)按刷新按鈕:無(wú)論 Cache-Control 為何值,都會(huì)重新訪問(wèn)服務(wù)器。
????? 通過(guò) Cache-Control 標(biāo)頭設(shè)置了緩存的頁(yè)面,再次向服務(wù)器請(qǐng)求時(shí)(比如按刷新按鈕),瀏覽器發(fā)出的請(qǐng)求頭部會(huì)添加 If-Modified-Since 標(biāo)頭,該標(biāo)頭向服務(wù)器詢問(wèn)從指定的日期后該頁(yè)面是否已經(jīng)修改。對(duì)于 ASP.NET 而言,根據(jù)頁(yè)面是否開(kāi)啟了服務(wù)器端緩存,響應(yīng)這樣的請(qǐng)求會(huì)有兩種結(jié)果,一種是該頁(yè)面開(kāi)啟了服務(wù)器端緩存并且該頁(yè)面緩存還未過(guò)期,則返回 304 的響應(yīng),這時(shí)瀏覽器使用本地緩存向用戶呈現(xiàn)頁(yè)面;其它情況則是重新處理該請(qǐng)求并啟動(dòng)新的 ASP.NET 頁(yè)面生命周期,返回 200 的響應(yīng)和新的頁(yè)面內(nèi)容。注意,請(qǐng)區(qū)別這里返回 304 響應(yīng)的情況和前述的不會(huì)重新訪問(wèn)服務(wù)器的情況的不同,后者根本就不向服務(wù)器發(fā)出請(qǐng)求。
????? 話題說(shuō)到這里,我們已經(jīng)把基于 HTTP 協(xié)議實(shí)現(xiàn)的頁(yè)面緩存進(jìn)行了說(shuō)明,但 ASP.NET 同樣也給我們提供了服務(wù)器端的緩存,這種緩存前面多次提到。為了說(shuō)明這點(diǎn),我們先把 HTTP 協(xié)議實(shí)現(xiàn)的緩存放到一邊,就是說(shuō)我們現(xiàn)在使用了 Cache-Control:no-cache 標(biāo)頭來(lái)進(jìn)行響應(yīng)。當(dāng)請(qǐng)求被 IIS 收到時(shí),ASP.NET 會(huì)根據(jù)頁(yè)面的緩存設(shè)置來(lái)決定如何響應(yīng),如果頁(yè)面沒(méi)有設(shè)置服務(wù)器緩存,則接下來(lái)的事情大家都是知道的;如果頁(yè)面設(shè)置了服務(wù)器緩存并且緩存沒(méi)有過(guò)期,則返回 200 的響應(yīng)和從服務(wù)器緩存中存儲(chǔ)的頁(yè)面內(nèi)容;如果頁(yè)面設(shè)置了服務(wù)器緩存但緩存已經(jīng)過(guò)期,則為該頁(yè)面啟動(dòng)新的 ASP.NET 頁(yè)面生命周期進(jìn)行處理,然后緩存新的頁(yè)面處理內(nèi)容并響應(yīng)到客戶端。
????? 使用 ASP.NET 開(kāi)發(fā)應(yīng)用程序,我們完全可以結(jié)合由 HTTP 協(xié)議實(shí)現(xiàn)的緩存和 ASP.NET 自身提供的緩存,具體的應(yīng)用當(dāng)然就得看實(shí)際的情況了。
關(guān)于 @ OutputCache 指令
????? 對(duì)于 @ OutputCache 指令已經(jīng)介紹了 Duration 和 VaryByParam 屬性,與“頁(yè)面輸出緩存的本質(zhì)”小節(jié)有關(guān)的屬性還有 Location 和 NoStore 屬性。NoStore 屬性接受一個(gè)布爾值,如果指定為 true,則向 Cache-Control 標(biāo)頭添加 no-store 設(shè)置。Location 屬性接受 OutputCacheLocation 枚舉值之一,用于控制資源的輸出緩存 HTTP 響應(yīng)的位置。
| 成員名稱 | 說(shuō)明 |
| Any | 輸出緩存可位于產(chǎn)生請(qǐng)求的瀏覽器客戶端、參與請(qǐng)求的代理服務(wù)器(或任何其它服務(wù)器)或處理請(qǐng)求的服務(wù)器上。此值對(duì)應(yīng)于 Cache-Control:public,并開(kāi)啟服務(wù)器端緩存。 |
| Client | 輸出緩存位于產(chǎn)生請(qǐng)求的瀏覽器客戶端上。此值對(duì)應(yīng)于 Cache-Control:private,并關(guān)閉服務(wù)器端緩存。 |
| Downstream | 輸出緩存可存儲(chǔ)在任何 HTTP 1.1 可緩存設(shè)備中,源服務(wù)器除外,這包括代理服務(wù)器和發(fā)出請(qǐng)求的客戶端。此值對(duì)應(yīng)于 Cache-Control:public,并關(guān)閉服務(wù)器端緩存。 |
| Server | 輸出緩存位于處理請(qǐng)求的 Web 服務(wù)器上。此值對(duì)應(yīng)于 Cache-Control:no-cache,并開(kāi)啟服務(wù)器端緩存。 |
| None | 對(duì)于請(qǐng)求的頁(yè)面,禁用輸出緩存。此值對(duì)應(yīng)于 Cache-Control:no-cache,并關(guān)閉服務(wù)器端緩存,這與在 ASPX 頁(yè)面上不包含 @ OutputCache 指令沒(méi)區(qū)別。 |
| ServerAndClient | 輸出緩存只能存儲(chǔ)在源服務(wù)器或發(fā)出請(qǐng)求的客戶端中,代理服務(wù)器不能緩存響應(yīng)。此值對(duì)于 Cache-Control:private,并開(kāi)啟服務(wù)器端緩存。 |
????? 現(xiàn)在針對(duì) @ OutputCache 指令的基本設(shè)置已經(jīng)介紹完成了,有關(guān)它的更多控制細(xì)節(jié)將在下一講中闡述,現(xiàn)在我們已經(jīng)可以與“頁(yè)面輸出緩存的本質(zhì)”小節(jié)中所講述的內(nèi)容結(jié)合起來(lái),有效的控制頁(yè)面輸出緩存了,如果僅是應(yīng)付一般的情況,到這樣就基本足夠了。
小結(jié)
?? ? ?本節(jié)初步揭開(kāi)了 ASP.NET 緩存技術(shù)的面紗,介紹了開(kāi)啟 ASPX 頁(yè)面輸出緩存的基本方法,但這不是本節(jié)的重點(diǎn),最需要關(guān)注的是 ASP.NET 緩存技術(shù)的實(shí)現(xiàn)細(xì)節(jié),“頁(yè)面輸出緩存的本質(zhì)”小節(jié)中對(duì)此有了說(shuō)明,但限于篇幅和作者的水平,并不詳盡。大家可以多多實(shí)踐,并使用 Fiddler 這樣的 HTTP 嗅探工具來(lái)深入研究這些細(xì)節(jié)。
轉(zhuǎn)載于:https://www.cnblogs.com/xupeng/archive/2011/01/08/1931968.html
總結(jié)
以上是生活随笔為你收集整理的ASP.NET 缓存技术(一)——启用页面输出缓存的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《数学之美与浪潮之巅》读后感
- 下一篇: Pod和容器设计模式