實作快取有兩種方式:儘可能使用 Internet 架構來達成快取, 或是撰寫程式以協助 Internet
架構來進行快取。撰寫快取演算法雖然相當有趣, 不過卻很可能徒勞無功。這是因為在 HTTP 要求鏈中,
有許多元素皆會快取資料。如果再自行設計快取功能, 可能會在不同階段重覆快取相同的資料, 對於整體效能並無幫助。
HTML 與 HTTP 快取指令
藉由 Internet 架構來管理快取機制, 亦可稱為使用「HTTP 逾期」(HTTP Expiration) 模型。有兩種方式可藉由
Internet 架構來控制快取:新增 HTML 標記, 或是新增 HTTP ID。
以下範例使用 HTML 標記來控制快取:
<html>
<head>
<title>Hanging Page</title>
<meta http-equiv="Cache-Control" content="max-age=3600">
<meta http-equiv="Expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
</head>
<body>
... |
HTML meta 標記具有兩個可模仿 HTTP ID 的屬性:http-equiv 與 content。使用 HTML meta
標記的問題在於, 這些標記是供 Web 瀏覽器之用, 因此無法在 XML 資料流中加入 meta 標記。亦即, 此種依賴 HTML 的快取機制,
不適用於非 HTML 的資料。
使用 Internet 架構來控制快取的第二種方式, 便是產生一組 HTTP 標記, 如下列 HTTP 要求結果所示:
HTTP/1.1 200 OK
Cache-Control: Public, max-age=3600
Expires: Wed, 10 Aug 2005 10:35:37 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 39
Date: Wed, 10 Aug 2005 09:35:37 GMT
Server: Apache-Coyote/1.1
<html><body>Hello world</body></html> |
Cache-Control 與 Expires 等兩個 HTTP ID 負責管理快取網頁的方式。Cache-Control
指定將內容存放在快取中 3600 秒 (1 小時)。Expires 定義擷取的內容何時逾期。Cache-Control 與 Expires
皆可讓 Proxy 或瀏覽器以「HTTP 逾期」模型來快取 HTTP 內容。
若是在 script 中使用, 可藉由下列 ASP.NET 程式碼來產生 HTTP ID:
<%@ Page Language = "C#" %>
<%@ Import Namespace="System" %>
<%
Response.Cache.SetExpires(DateTime.Now.AddMinutes( 60 ) ) ;
Response.Cache.SetCacheability(HttpCacheability.Public) ;
%>
<html>
<head>
<title>Cached Page</title>
</head>
<body>
Hello world!
</body>
</html> |
在 .NET 中, SetExpires 與 SetCacheability 方法會新增 Expires 與 Cache-Control
ID。若要使用 Java Servlet 來達成相同效果, 請參考下列程式碼:
import javax.servlet.http.*;
import javax.servlet.*;
import java.io.*;
import java.util.*;
public class GenerateHeader extends HttpServlet {
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
resp.addHeader("Cache-Control", "Public, max-age=3600");
resp.addHeader("Expires",
"Fri, 30 Oct 2006 14:19:41 GMT");
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("<html><body>Hello world</body></html>");
}
} |
HTTP 逾期快取的缺點
一般而言, 不建議使用「HTTP 逾期」模型, 而是推薦使用「HTTP 驗證」(HTTP Validation) 模型, 亦即, 撰寫程式以協助
Internet 架構來進行快取。
以下例子可說明「HTTP 逾期」模型的缺點。假設某一流量不大的新聞網站, 若啟用 HTTP 快取並指定 30 分鐘逾時
(此設定乃是為了方便說明而任意指定)。這意謂著瀏覽器下載內容後, 要在 30 分鐘之後才有可能再下載該網頁的新內容。對於新聞網站而言, 30
分鐘內可能會有許多新的新聞, 因此這顯然並非很好的做法。當然, 使用者可以自行清空快取, 以便下載最新版本的網頁。不過,
很可能是每次都下載同樣的內容, 這也使得快取毫無用武之地。Java Servlet/JSP 或 ASP.NET 網頁的 script,
便經常使用此一策略, 而造成網站效能低落。
建議方式:HTTP 驗證
比較好的方式是使用「HTTP 驗證」(HTTP Validation) 模型。此模型在傳送回應時, 會一併傳送「票證」(Ticket),
藉以識別回應資料。若用戶端再次下載資料, 會將前次下載的票證, 一併傳送給伺服器。伺服器比較用戶端所傳來的票證與目前的票證, 若兩者相同,
便傳回 HTTP 304, 代表內容並未變更。此時, 用戶端只要從快取中擷取內容即可。「HTTP 驗證」模型仍然需要傳送 HTTP 要求,
但可避免重覆產生與傳送相同的內容。
若實作「HTTP 驗證」模型, 將會有如下的 HTTP 交談。
此範例中, 用戶端送出要求, 而伺服器則傳回回應。
要求 1:
GET /ajax/chap04/cachedpage.html HTTP/1.1
Accept: */*
Accept-Language: en-ca
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;
.NET CLR 2.0.50215)
Host: 127.0.0.1:8081
Connection: Keep-Alive |
回應 1:
HTTP/1.1 200 OK
ETag: W/"45-1123668584000"
Last-Modified: Wed, 10 Aug 2005 10:09:44 GMT
Content-Type: text/html
Content-Length: 45
Date: Wed, 10 Aug 2005 10:11:54 GMT
Server: Apache-Coyote/1.1
<html>
<body>
Cached content
</body>
</html> |
用戶端針對 /ajax/chap04/cachedpage.html 發出要求。伺服器將內容傳回, 但其中並無 Cache-Control
或 Expires 等 ID。乍看之下, 會以為傳回的內容將不會存入快取, 事實並非如此。伺服器已表明使用「HTTP 驗證」模型,
而非「HTTP 逾期」模型。傳回的網頁將存入快取, 並以 ETag 做為識別的 ID。ETag 亦稱為「實體標記」(Entity tag),
就好比是 HTML 網頁的雜湊值。實體標記的前置字母 W 代表網頁為弱參考 (Weak reference), HTTP
伺服器可能不會即時反映伺服器端的網頁更新。
下一步則是重新整理瀏覽器, 以要求同一網頁。HTTP 交談如下。
要求 2:
GET /ajax/chap04/cachedpage.html HTTP/1.1
Accept: */*
Accept-Language: en-ca
Accept-Encoding: gzip, deflate
If-Modified-Since: Wed, 10 Aug 2005 10:09:44 GMT
If-None-Match: W/"45-1123668584000"
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;
.NET CLR 2.0.50215)
Host: 192.168.1.100:8081
Connection: Keep-Alive |
回應 2:
HTTP/1.1 304 Not Modified
Date: Wed, 10 Aug 2005 10:11:58 GMT
Server: Apache-Coyote/1.1 |
當用戶端發出第二個要求時, 會一併額外傳送 If-Modified-Since 與 If-None-Match 等 ID。請注意
If-None-Match 參考先前所傳送的 ETag 值。伺服器會查詢 URL 並產生實體標記。若實體標記與用戶端傳來的值相同, 則傳回
HTTP 304, 指示內容並未變更。
用戶端除了使用 If-None-Match 之外, 也可使用 If-Match。若用戶端傳送 If-Match, 且資料已過時,
則伺服器將傳回「快取未中」(Cache miss) 的錯誤, 而非傳回新資料。若用戶端傳送 If-None-Match,
當伺服器上的資料未變更時, 伺服器將傳回 HTTP 304。若資料已過時, 伺服器便傳回新資料。
使用「HTTP 驗證」模型的優點, 在於用戶端發出要求後永遠可取得最新的資料, 同時又能保證使用最少量的 HTTP 通訊。
雖然如此,「HTTP 逾時」模型仍適用於某些情況下, 例如, 靜態且很少更新的 HTML 網頁。但是 Ajax 應用程式大多使用動態資料,
因此「HTTP 驗證」模型才是比較合理的選擇。