靜態內容實現緩存的七種不同方法

靜態內容實現緩存的七種不同方法

在商業世界中,人們常說“現金為王” 。然而,在技術世界里,我們卻說“緩存為王” 。從瀏覽器到應用前端、應用后端、數據庫,每一層都可以通過緩存來顯著地提高系統的擴展能力 , 改善系統的響應能力 , 同時減少系統的負擔 。
互聯網平臺上的內容可以分為靜態和動態兩種 。靜態內容指那些不經常改變的文本和圖像 。動態內容是指隨著時間的推移,不斷變化的內容 。本文主要討論靜態內容實現緩存的七種不同方法 。
1. 利用 CDN 實現緩存
CDN,即內容分發網絡,是通過骨干網絡把一組計算機連接起來 , 存儲客戶數據或內容的副本 。通過在不同的網絡,策略性地通過部署邊緣服務器和應用大量的技術和算法,把用戶的請求指定到最佳響應節點上 。這種優化的邏輯可以是基于最少網絡跳轉數量、最高系統可用性或最少的請求數量 。這種優化常常聚焦在減少最終用戶、請求者或服務可以感知的響應時間 。
正如本例所示,在網站服務器前使用CDN的效果是,CDN負責處理所有的請求,只有當需要查詢緩沖內容是否更新時才會訪問源服務器 。因此,只需要購買少量低配置的服務器和網絡帶寬 , 以及少數維護基礎設施的人員 。無論網站的網頁是動態還是靜態,都可以考慮加入CDN形成混合緩存 。該層緩存可以提供快速交付的好處 , 通常有非常高的可用性,而且網站服務器處理更少的流量 。
2. 利用 HTTP 頭來靈活管理緩存
HTTP 頭提供了有關代理緩存的有效控制,這些 HTTP 頭在 HTML 中看不到,而是由網絡服務器或生成頁面的代碼動態生成 。通過服務器配置或代碼來控制 。一個典型的 HTTP 響應頭看起來可能像這樣:

HTTP Status Code: HTTP/1.1 200 OK
Date: Thu, 21 Oct 2015 20:03:38 GMT
Server: Apache/2.2.9 (Fedora)
X-Powered-By: PHP/5.2.6
Expires: Mon, 26 Jul 2016 05:00:00 GMT
Last-Modified: Thu, 21 Oct 2015 20:03:38 GMT
Cache-Control: no-cache
Vary: Accept-Encoding, User-Agent
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
與緩存最相關的頭是 Expires和Cache-Control。Expires 實體頭字段提供響應有效期信息 。如果想要把響應標記為“永不過期”,源服務器就應發送從響應時間算起一年后的日期 。在前面例子中 , 注意到 Expires 頭標識日期為 2017 年 5 月 12 日 05:00GMT 。如果今天是 2017 年 4 月 12 日,請求的頁面將在大約一個月后過期,瀏覽器應在那個時候從服務器獲取數據以刷新內容 。
Cache-Control 通用頭字段,用于按 RFC2616 第 14 節定義的 HTTP1.1 協議定義指令 , 沿請求/響應鏈的所有緩存機制必須遵守這些指令 。該頭可以發出許多指令 , 包括 public、private,、no-cache 和 max-age 。如果響應同時包含Expires頭和 max-age 指令,即使 Expires 限制較多,max-age 指令的優先級同樣高過 Expires 頭 。以下是一些 Cache-Control 指令的定義:
public—響應可以由任何緩存、共享或非共享緩存來處理 。
【靜態內容實現緩存的七種不同方法】private—響應針對單用戶 , 不能放在由共享緩存 。
no-cache—在與源服務器確認之前 , 不得使用緩存來滿足后續的其他請求 。

max-age—如果當前數值大于在請求時給定的值(秒),那么響應過時 。
設置 HTTP 頭有幾種方式,包括通過網絡服務器和代碼 。Apache2.2 的配置設置在 httpd.conf 文件 。Expires 頭要求把 mod_expires 模塊添加到 Apache 。Expires 模塊有三條基本指令 。第一條 ExpiresActive 告訴服務器激活該模塊 。第二條指令 ExpiresByType 設置 Expires 服務特定類型的對象(如圖片或文本) 。第三個指令 ExpiresDefault 設置如何處理所有未指定類型的對象 。參見下面的代碼示例:
ExpiresActive On
ExpiresByType image/png "access plus 1 day"
ExpiresByType image/gif "modification plus 5 hours"
ExpiresByType text/html "access plus 1 month 15 days 2 hours"
ExpiresDefault "access plus 1 month"
在 HTTP 設置 Expires、Cache-Control 和其他頭的另外一種方法是在代碼中實現 。PHP 直接利用 header() 命令發送原始的 HTTP 頭 。在任何輸出前必須通過 HTML 標簽或從 PHP 代碼調用 header() 命令 。關于頭設置,參見下面的 PHP 示例代碼 。其他語言也有類似的頭設置方法 。
header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("cache-control: no-store, no-cache, must-revalidate");
header("Pragma: no-cache");
?>
最后一個主題涉及到調整網絡服務器的配置 , 以優化其性能與可擴展性 。keep-alives 或 HTTP 持久連接允許多個 HTTP 請求復用 TCP 連接 。在 HTTP / 1.1 中,所有的連接都是持久的,大多數網絡服務器默認允許保持連接 。根據 Apache 文檔記載,使用連接保持可以使 HTML 頁面延遲減少了 50% 。在 Apache 的 httpd.conf 文件中 keep-alives 的默認設置為打開,但 KeepAliveTimeOut 的默認值只設置為 5 秒 。超時設置較長的好處是不必建立、使用和終結 TCP 連接就可以處理更多的 HTTP 請求,超時設置較短的好處是網絡服務器的線程不會被捆綁?。?可以繼續服務其他請求 。根據應用或網站的具體情況在兩者之間尋找平衡點很重要 。

有一個實際的例子 , 利用 AOL 研發的開源的網頁測試工具 webpagetest.org , 對某網站做了一個測試 。測試對象是一個運行在 2.2 版的 Apache HTTP 服務器上的簡單 MediaWiki 。
3. 利用 Ajax 實現緩存
2005 年杰西·詹姆斯·加勒特在他的文章《 Ajax :一種網絡應用的新方法》中創造了 Ajax 這個術語 。Ajax 是 Asynchronous JavaScript and XML 的縮寫 。雖然我們經常把它作為一種技術,但更為貼切的描述是一組技巧、語言和在瀏覽器(或客戶端)上使用的方法,有助于為最終用戶帶來更豐富內容和更實時體驗 。
因為可以減少數據在網絡上不必要的往復傳遞,從而使用戶與瀏覽器之間的互動更容易,用戶交互因此可以更迅速地發生 。用戶不用等待服務器的響應就可以放大或縮小圖片,下拉菜單可以根據以前的輸入預先安排好,當用戶在搜索欄輸入查詢關鍵詞時,就可以開始看到那些可能會感興趣并起到引導作用的潛在搜索詞 。Ajax 的異步特性還可以幫助我們,在往客戶端瀏覽器加載郵件時,可以根據用戶的某些動作來判斷是否要繼續接收郵件,而不必等用戶點擊“下一頁”按鈕 。
但是其中的一些動作不利于平臺的擴展,以用戶在網站上輸入某個特定商品的搜索關鍵詞為例 。我們可能想用商品目錄來填充搜索建議,即那些當用戶鍵入搜索條件時出現的關鍵詞 。Ajax 可以通過用戶后續的每個按鍵向服務器發送請求,根據已鍵入的詞而返回搜索結果,并在不需要用戶介入刷新瀏覽器的情況下,把搜索結果填充到下拉菜單 。有可能返回的是基于用戶不完全鍵入的字符串而獲得的完整搜索結果!許多搜索引擎和電子商務網站都可以找到這種實施的例子 。以每個后續按鍵為基礎最終形成搜索服務器需要的查詢語句 , 可能既昂貴也浪費我們的后臺系統資源 。例如 , 當用戶輸入 “Beanie Baby” 時,可能會帶來連續 11 次的搜索,其實真正只需要一次 。用戶的體驗可能很奇妙,但如果用戶按鍵速度很快,在打完字前,實際上多達 8-10 次的搜索可能永遠沒有機會返回結果 。
我們的目標是減少在網絡上來回傳輸數據,以減少用戶感知的響應時間和降低服務器的負載 。因此,響應頭中的 Expires 設置的有效期應足夠長,這樣 , 瀏覽器會在本地緩存第一次查詢的結果,并在后續請求中反復使用 。靜態或半靜態的對象,如公司商標或者簡介圖片,其有效期應該設置成幾天或更長 。某些對象的時間敏感性可能很強 , 如閱讀好友的狀態更新 。在這些情況下,應該把 Expires 頭設置為數秒甚至數分鐘,以給用戶實時的感覺 , 同時降低整體的負載 。
數據集是靜態甚至半動態的情況(例如 , 有限的或上下文敏感的產品目錄)很容易解決 。從客戶端看 , 以異步的方式獲取這些結果,然后緩存起來供同一客戶端以后使用,或者更重要的是確保 CDN、中間緩存或代理存儲它們,以利于其他的用戶進行類似的搜索 。


經驗總結擴展閱讀