ASP.NET 內建的 OutputCache 是針對所有使用者的伺服器端緩存,能從廣度上提升並發處理能力。而本文要介紹的瀏覽器緩存,則是針對單一使用者,在特定場景下將瀏覽器納入緩存機制,從深度上最大化動態頁面的響應速度。若能善用 HTTP 標頭中的 Last-Modified 與 If-Modified-Since,即可讓瀏覽器在緩存有效期內直接讀取本地資料,完全避免向伺服器發送請求,大幅降低伺服器負載。
當瀏覽器首次請求一個靜態頁面(如 test1.htm),伺服器會回應 200 OK,並附帶以下關鍵標頭:
Fri, 09 Sep 2011 02:56:45 GMT。瀏覽器會將這些資訊連同頁面內容一起存入本地緩存(可在 Firefox 中輸入 about:cache?device=disk 查看)。
再次請求同一 URL 時,瀏覽器會在請求標頭中帶上 If-Modified-Since(值為上次收到的 Last-Modified)或 If-None-Match(值為 ETag)。伺服器比對後,若資源未變更,回應 304 Not Modified,瀏覽器即直接讀取本地緩存,省去下載內容的頻寬與時間。
預設情況下,ASPX 動態頁面不會輸出 Last-Modified 標頭,導致瀏覽器每次都重新請求完整內容(始終返回 200 OK)。
在 Page_Load 中主動加入標頭:
protected void Page_Load(object sender, EventArgs e)
{
this.Response.AddHeader("Last-Modified", DateTime.Now.ToString("U", DateTimeFormatInfo.InvariantInfo));
}
此時回應標頭已包含 Last-Modified,請求標頭也會出現 If-Modified-Since,但因伺服器未邏輯判斷,仍會返回 200 OK。
protected void Page_Load(object sender, EventArgs e)
{
// 1. 輸出 Last-Modified(GMT 時間)
this.Response.AddHeader("Last-Modified", DateTime.Now.ToString("U", DateTimeFormatInfo.InvariantInfo));
// 2. 解析瀏覽器送來的 If-Modified-Since
DateTime ifModifiedSince;
if (DateTime.TryParse(this.Request.Headers.Get("If-Modified-Since"), out ifModifiedSince))
{
// 注意:If-Modified-Since 是 GMT,本地若為 GMT+8 需加 8 小時再比較
if ((DateTime.Now - ifModifiedSince.AddHours(8)).TotalSeconds < 10)
{
Response.Status = "304 Not Modified";
Response.StatusCode = 304;
Response.End(); // 直接結束,不輸出內容
return;
}
}
// 3. 正常業務邏輯(模擬耗時操作,如查詢資料庫)
// ...
}
測試結果:10 秒內持續請求皆返回 304,瀏覽器零流量讀取緩存;10 秒後返回 200 重新獲取資料。
模擬 100 並發、1000 次請求,加入 -H "If-Modified-Since: ..." 模擬瀏覽器行為:
ab -n1000 -c100 -H "If-Modified-Since: Friday, 09 September 2011 09:35:23 GMT" https://your-site/test1.aspx
左側無緩存,右側啟用緩存,吞吐率相差 10 倍以上。
上述機制仍需瀏覽器發送請求並經伺服器判斷。下一篇將介紹 HTTP Expires / Cache-Control,讓瀏覽器在緩存有效期內完全不發請求,進一步消除伺服器處理開銷。