Caching in HTTP
我們都知道 Cache (快取) 的目的就是讓系統反應變快,理所當然快取的概念也同樣設計在 HTTP。HTTP 1.1 是遵循 REST (Representational state transfer) 的架構進行設計,我們先回顧一下 Architectural Styles and the Design of Network-based Software Architectures (by Roy Thomas Fielding) 這一篇文章中,所提到的 Cache 概念。在「CHAPTER 5. Representational State Transfer (REST)」這一個章節中有明確提及 Cache 這個角色在 REST 架構中的位置,如下圖:
上圖中所代表的「$」就是 Cache 的意思,上圖的架構是把 Cache 放在 Client 中,Client 各位可以想像為瀏覽器。
上圖是表示比較複雜的 Cache 架構,我們可以看到在上圖中 Server 與 Client 都保留了 Cache 的能力,算是比較靈活的應用情境。圖中「圓柱體」符號為 Connector,這樣的架構中 Cache 由 Connector 進行實作。我們可以把 Connector 想像為瀏覽器核心負責發送 Socket 連線的元件,可以從這個角色直接實作 Cache 機制。
回到 HTTP 1.1 所設計的 Cache 機制,主要的目的為「降低 Request 發送」與「減少 Response 資料」,因此設計了以下兩種機制來達成目的:
- Expiration, 用來處理 Cache Data 的時間有效期限
- Validation, 用來驗證 Cache Data 的內容有效機制
這些機制在 HTTP 的實作上,是在 Client 與 Server 之間透過 Header 的「交互作用」來完成,為什麼說是「交互作用」呢?看看前面介紹的 REST Cache 架構圖,其實 Cache 可以在 Client 或 Server 實作,這樣的設計讓 Cache 更具彈性。
HTTP 1.1 Cache Header
其實早在 HTTP 1.0 就已經設計了 Cache 機制,但是使用的 HTTP Header 不一樣。HTTP 1.0 是透過 Expires 與 Pragma 這兩個 Header 進行控制,然而 HTTP 1.1 主要是透過「Cache-Control」這個 Header 提供快取機制,比起 HTTP 1.0 的設計,HTTP 1.1 更加彈性與靈活。其中用來描述 HTTP 1.1 的 RFC 2616 文件中分別定義了 Request 與 Response 可以使用的快取策略,基本定義如下:
- Request:no-store, max-age, max-stale, min-fresh, no-transform, only-if-cached
- Response:public, private, no-cache, no-store, no-transform, must-revalidate, proxy-revalidate, max-age, s-maxage
HTTP 1.1 除了用到「」Header 進行控制之外,還提供了以下兩種機制來確保 Cache 資料的正確性 (Validation),分別為「Last-Modified / If-Modified-Since」與「ETag / If-None-Match」。第一種是透過資料 (通常用檔案) 的修改時間來驗證有效性,採用 GMT 時間,最小單位是秒。第二種是透過資料內容來驗證,主要利用資料的 Hash 值來比對,像是 Apache 就是利用 File System 的 iNode Hash 來達成,不然每次都計算 Hash 太傷成本。
Cache-Control Response Header 介紹
在實際的使用上,上面章節提到的關鍵字是可以合併使用的,接下來我們介紹幾個常用的 Cache 方式,透過 Chrome 瀏覽器的行為來跟各位介紹。
- Cache-Control: no-store, 這個最簡單,就是完全不 Cache 的意思。參考以下圖片說明:
- Cache-Control: no-cache + Last-Modified: GMT, 接下來我們測試傳說中的 304 Cache,在這裡定義 no-cache 並非完全不 Cache,no-cache 只是 Server Response 建議的策略。在我使用 Chrome 瀏覽器的測試下,如果 Server 有一併送出「Last-Modified」或「ETag」,那麼瀏覽器下次再詢問時依然會夾帶「If-Modified-Since」或「If-None-Match」,當 Server 回傳 304 Not Modified 時,瀏覽器就會直接使用 Cache 資料,達到「減少 Response 資料」這個目的。流程說明如下圖:
- Cache-Control: max-age=秒, 上面的情境雖然「減少 Response 資料」但還是發送了 Request 來驗證 Cache 的有效性,那麼用 max-age 來定義資源期限便可以解決這樣的問題。當有定義 max-age 時,一但有效期限還沒到,Connector 並不會發送連線,而是直接回傳 Cache 資料,以達成「降低 Request 發送」這個目的。請參考下圖說明:
- Cache-Control: must-revalidate, 這個算是比較複雜的 Cache 策略,會交由 Client 自行判斷,經過 Chrome 測試,如果是重新整理 (或按 F5) 就送發送 Request 詢問 Cache 有效性;若是在網頁間「點連結」或「上一頁」則會直接使用 Cache 資料。如下圖:
- Cache-Control: private, 這個情境用在敏感資料的處理策略,通常在關閉瀏覽器或切換作業系統使用者的過程就會被刪除。如下如:
- Cache-Control: public, 這種策略的 Cache 是可以讓其他使用者共用的,通常用在比較公開的資料,剛好跟 private 相反。以往經驗在 IE 瀏覽器不可與 HTTPS 搭配使用,會衝突造成許多奇怪的現象。請參考下圖:
討論
在我們一般的實務應用上,其實多多利用 no-store, max-cache, no-cache, must-revalidate 這幾種策略進行搭配使用,已經可以提昇不少網頁的反應速度與使用者觀感。在使用 Cache 時更特別要注意資料的有效性,避免過度的 Cache 造成即時資訊的顯示錯誤,在設計時要更加小心使用。
HTTP 1.1 對於 Cache 的設計相當完整,這篇文章大概只談到 10% 左右的內容,其實 HTTP 還有很多不同的 Cache 應用,由於本人技術水平與英文閱讀能力有限,無法為大家一一介紹,希望後繼有強者能夠提出指正與補充。感謝。
參考資料
- Architectural Styles and the Design of Network-based Software Architectures
- HTTP 1.1 RFC 2616 - 13 Caching in HTTP
- HTTP 1.1 RFC 2616 - 14.9 Cache-Control