》我們通過OutputCache,讓請求去通路伺服器asp.net的輸出緩存,我們擴充了OutputCacheProvider,這相當于是通路伺服器上的靜态資源。OutputCache是針對所有通路伺服器資源的使用者,本篇要介紹的浏覽器緩存則是針對單個使用者,讓浏覽器在我們的控制下徹底不持續通路伺服器上的動态内容,也就是我們要讓浏覽器變成我們的緩存機制中的一部分,在某些特定的場景下最大化地提升ASP.NET站點的性能。如果說OutputCache是從廣度上提升并發效率,則浏覽器緩存是從深度上提升效率。
一:HTTP頭簡介
1.1浏覽器第一次請求
假設我們請求一個URL位址,譬如我伺服器上的一個靜态頁面
http://192.168.0.77/luminji2/html/test1.htm ,會傳回如下的HTTP頭資訊:
HTTP頭資訊中每個參數的含義這裡暫且不表,我們關注和本文論述相關的3個資訊:
首先,響應狀态200OK,即表示從伺服器上抓取資料成功。
其次,Last-Modified:
Fri, 09 Sep 2011 02:56:45 GMT
這是WEB伺服器在告訴浏覽器,我這個檔案的最後修改日期是
Fri, 09 Sep 2011 02:56:45 GMT。必須要說明的是,它是GMT時間,也就是格林威治時間,一般中國境内使用的是GMT+8時區(要看系統的區域設定而定)。
最後就是Etag,
WEB伺服器目前響應的這個對象的标志值,就一個對象而言,比如一個 html 檔案,如果被修改了,其 Etag 也會别修改。 1.2什麼是浏覽器緩存
我使用的是FireFox,在位址欄内敲入about:cache?device=disk,我們就會看到被浏覽器緩存起來的上面這個HTML頁面,如下:
(注意,這裡的Last Modified和Http頭中的Last-Modified沒有任何關系)。
每種浏覽器都會有自己的緩存機制,但是都差不多,這裡暫且不表。
1.3如何命中緩存
再次請求剛才的URL,我們得到頭資訊如下:
可以看到狀态變為304 Not Modified了,這相當于是WEB伺服器告訴浏覽器,請用自己的緩存,不要到我這裡來下載下傳正文内容。那麼,WEB伺服器是根據什麼來決定這樣告訴浏覽器呢?
這裡,我們就需要請求頭資訊中的If-Modified-Since。請求頭是浏覽器發送給WEB伺服器的,一旦包含這個參數,就是浏覽器跟WEB伺服器說:請檢視自從
Fri, 09 Sep 2011 02:56:45 GMT一來,你的内容變動過沒。WEB伺服器就會根據這個來判斷,如果沒有變動過,就會給浏覽器傳回304 Not Modified,就像本例。這樣子,浏覽器就會去本地拿正文資料,減少了網絡流量。
If-None-Match就是Etag判斷模式,跟Last-Modified其實完成的目的是一緻的,這裡暫且不表。
假設我們修改了檔案test1.htm,試想會是什麼結果,肯定是200OK。浏覽器和WEB伺服器之間就是通過這種機制來完成靜态網頁的緩存。
二:asp.net的浏覽器緩存實作
上面我們說的是靜态頁面的情況,那麼aspx頁面,也就是動态頁面會是怎麼樣一種情況?現在,我們建立一個動态網頁來看一看。先來建立一個最簡單的aspx,如下:
<body>
<%=DateTime.Now %>
</body>
請求一下,得到的頭資訊如下:
然後再多次請求一下,我們發現每次都是200OK,并且我們發現頭資訊中丢了一個很重要的資訊,那就是Last-Modified。伺服器沒有告訴浏覽器自己的對象的最後修改日期,那麼浏覽器就隻好每次去伺服器重新擷取全部資料了。看到這裡,我們應該明白了,要讓浏覽器不去拿資料,動态程式就得想法設法自己添加這個頭資訊。
好的,現在我們就在ASPX的背景代碼中這樣來實作一個最簡單的頭資訊添加:
protected void Page_Load(object sender, EventArgs e)
{
this.Response.AddHeader("Last-Modified", DateTime.Now.ToString("U", DateTimeFormatInfo.InvariantInfo));
}
添加了頭資訊後,我們發現再次請求URL後,頭資訊變為如下:
可以欣喜滴看到,響應頭中包含了Last-Modified,而請求頭中則包含了If-Modified-Since。
當然,我們仍舊發現,每次請求還是200OK。這是當然了,既然頭資訊都是ASP.NET在背景添加的,那麼我們要傳回什麼樣的響應狀态給伺服器這段邏輯也得由自己來寫。現在,我們假設我們要讓浏覽器緩存10秒的時長,其完整的代碼應該如下:
protected void Page_Load(object sender, EventArgs e)
{
this.Response.AddHeader("Last-Modified", DateTime.Now.ToString("U", DateTimeFormatInfo.InvariantInfo));
DateTime IfModifiedSince;
if (DateTime.TryParse(this.Request.Headers.Get("If-Modified-Since"), out IfModifiedSince))
{
if ((DateTime.Now - IfModifiedSince.AddHours(8)).Seconds < 10)
{
Response.Status = "304 Not Modified";
Response.StatusCode = 304;
return;
}
}
//其它
}
經過這次修改後,如果我們在10秒内持續請求該aspx頁面,則始終傳回304狀态,也即浏覽器不會去伺服器拿正文,隻會在本地去讀取自己的緩存,這樣一來,伺服器壓力自然就小了。如果我們10秒内不對伺服器請求這個頁面,則10秒後會傳回200OK,即重新到伺服器拿頁面資料。
現在,用AB來模拟100并發使用者進行1000次請求,得到的比較結果如下(注意,為了強化效果,我們在背景需要模拟一些比較耗時的動作,比如讀取資料庫):
左邊是未做緩存的aspx頁面,右邊是做了緩存的aspx頁面,可以看到,吞吐率相差10倍之多。
提示,使用ab進行壓力測試的時候,需要加入If-Modified-Since的頭資訊,指令如下:
C:\>ab -n1000 -c100 -H "If-Modified-Since: Friday, 09 September 2011 09:35:23 GMT"
http://192.168.0.77/luminji2/aspx/test1.aspx本文代碼下載下傳:
MvcApplication320110909.rar三:問題的提出
在上面的說到的浏覽器緩存實作中,浏覽器通過和WEB伺服器的溝通協調機制來确定自己是否需要調用緩存,這意味着動态程式仍舊需要處理來自用戶端的請求,如果有一種機制能夠讓浏覽器不需要請求伺服器就能夠決定是否調用緩存,就能徹底舍去伺服器處理這一環節。下一篇将繼續闡述這種機制。
ASP.NET性能優化之上一篇:
本文基于
Creative Commons Attribution 2.5 China Mainland License釋出,歡迎轉載,演繹或用于商業目的,但是必須保留本文的署名
http://www.cnblogs.com/luminji(包含連結)。如您有任何疑問或者授權方面的協商,請給我留言。