Web 緩存
Web 緩存的作用與類型
-
數據庫緩存
-
memcached
redis
-
服務端緩存
-
代理服務器緩存
squid
-
CDN 緩存 (網關緩存 反向代理緩存)
-
瀏覽器端緩存
-
Web 應用邏輯層緩存 (代碼邏輯實現)
Web 緩存機制 (瀏覽器端)
根據 Meta 標籤和 HTTP Header 頭來決定是否緩存 (根據 新鮮度 (過期機制) 和 校驗值 (驗證機制 Etag))
有關緩存的消息報頭
緩存優先級
-
Cache-Control(控制更細緻) > Expires
-
Cache-Control/Expires > Last-Modified/ETag 常配合使用
-
ETag > Last-Modified
用戶操作行爲與緩存
無法緩存的請求
-
HTTP 信息頭中包含 Cache-Control:no-cache,pragma:no-cache,或 Cache-Control:max-age=0 等告訴瀏覽器不用緩存的請求
-
需要根據 Cookie,認證信息等決定輸入內容的動態請求是不能被緩存的
-
經過 HTTPS 安全加密的請求(有人也經過測試發現,ie 其實在頭部加入 Cache-Control:max-age 信息,firefox 在頭部加入 Cache-Control:Public 之後,能夠對 HTTPS 的資源進行緩存,參考《HTTPS 的七個誤解》)
-
POST 請求無法被緩存
-
HTTP 響應頭中不包含
Last-Modified/Etag
,也不包含Cache-Control/Expires
的請求無法被緩存
使用緩存
-
同一資源保證 URL 穩定
-
給
CSS JS 圖片
等靜態資源增加HTTP緩存頭
, 並強制入口不被緩存 -
減少對
Cookie
的依賴 -
HTTPS
加密協議儘量不對可緩存資源使用 (因爲HTTPS
默認不緩存, 必須配置才能緩存) -
不涉及敏感信息的資源儘量採用
GET
方式請求 -
動態內容緩存
-
動態腳本定期將內容導出成靜態文件, 讓 Web 可直接訪問
-
動態腳本的響應頭增加
Cache-Control:max-age
, 告訴瀏覽器過期前可以直接使用副本 -
通過代碼給動態腳本的響應頭添加
Last-Modified/Etag
信息,瀏覽器再次請求的時候,可以通過解析If-Modified-Since/If-None-Match
得知瀏覽器是否存在緩存,由代碼邏輯控制是否返回 304
服務器配置
Apache Nginx 進行緩存頭配置
動態腳本配置
服務端代碼 Header 頭增加 Expires/Cache-Control/Etag
等信息, 更精細的控制緩存效果
禁用緩存
方法 1 在 meta 標籤標明
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
方法 2 動態 setRequestHeader
cache-control='no-cache,no-store'
pragma='no-cache'
if-modified-since=0;
方法 3 請求端設置if-modified-since
爲已經過期的某個時間,可以是幾年前或者幾十年前。
方法 4 服務端設置 Expires 爲過期某個時間
需要一致性檢測則儘量去配合 Etag 以及 last-Modified 去進行比較然後返回使用緩存還是新數據
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
方法 3 url 後面加隨機數或者時間戳
url += “&random=” + Math.random()
Ajax 緩存
-
Ajax
緩存和HTTP
緩存效果相同, 只緩存GET
請求 -
IE
瀏覽器不會刷新過期日期前的Ajax
內容, 只能強制清空緩存 -
Ajax
禁用緩存方式與HTTP
禁用緩存方式相同
非冪等請求緩存 (POST)
一些idempotent request
並不能通過Get
來實現的時候,例如,搜索 API 通常會需要很多的參數,尤其是那些擁有很多屬性的產品,而這些屬性都必須通過參數來傳遞。問題來了,如果請求攜帶的參數超過了 GET 請求的限制長度怎麼辦?
HTTP 的協議規範允許滿足下列條件中其一的以緩存來響應:
-
緩存的響應與原始服務器的響應是一致的,簡而言之就是說代理可以保證緩存的響應和服務器的響應之間的語義等價。
-
到客戶端的響應的新鮮度是可以接受的
-
到客戶端的響應的新鮮度不可接受,但是附加了合適的警告。
解決方式:
將 POST 的內容(附帶一部分頭信息),做一個摘要,將摘要附在 URL 後面,使用這個來作爲緩存的 key。換句話說,緩存主鍵被修改爲包括 URL 以及一些請求體,後續的擁有相同的請求體的請求將會命中緩存。在實踐的過程中我添加了一些頭腦保證緩存的唯一性。
-
區分 idempotent request 的 post 請求和非 idempotent request 的 post 請求
-
在代理中配置 URL 的匹配模式,代理如果匹配到非 idempotent request 請求就不緩存。
-
在頭中添加 context-aware 以區分不同的請求
-
基於緩存邏輯的一些命名約定,例如 API 的名稱以 set、add、delete 等開頭的就不被緩存。
-
處理非 idempotent request 的 post 請求
-
如果 URL 在 “DO NOT CACHE” 列表中
-
如果摘要不匹配
-
過了緩存的有效時間
-
任何收到需要重新驗證的請求的時候
優勢
-
加快了重複請求的效率,減少了代理到原服務器之間的往返
-
一個用戶的請求不僅可以用作緩存該用戶的後續請求,其他用戶也可共享
-
節省了代理和原服務器之間的帶寬
這個解決方案的變種可以用在正向代理和反向代理,甚至兩者都用
HTML5 離線緩存
Manifest
-
在服務器上添加 MIME TYPE 支,讓服務器能夠識別 manifest 後綴的文件
AddType text/cache-manifest manifest
-
創建一個後綴名爲
.manifest
的文件,把需要緩存的文件按格式寫在裏面,並用註釋行標註版本
CACHE MANIFEST
# 直接緩存的文件
CACHE:
Path/to/cache.js
# version:2012-03-20
- 給
<html>
標籤加manifest
屬性,並引用manifest
文件
<html manifest=”path/to/name-of.manifest”>
離線應用訪問及更新流程
-
第一次訪問離線應用的入口頁 HTML(引用了 manifest 文件),正常發送請求,獲取 manifest 文件並在本地緩存,陸續拉取 manifest 中的需要緩存的文件
-
再次訪問時,無法在線離線與否,都會直接從緩存中獲取入口頁 HTML 和其他緩存的文件進行展示。如果此時在線,瀏覽器會發送請求到服務器請求 manifest 文件,並與第一次訪問的副本進行比對,如果發現版本不一致,會陸續發送請求重新拉取入口文件 HTML 和需要緩存的文件並更新本地緩存副本
-
之後的訪問重複第 2 步的行爲
離線機制的緩存用途
從 Manifest 的機制來看,即使我們不是爲了創建離線應用,也同樣可以使用這種機制用於緩存文件,可以說是給 Web 緩存提供多一種可以選擇的途徑。
存在的問題
-
緩存文件更新控制不靈活
-
一些
http
頭設置會導致 manifest 無法正常更新 -
Manifest 中緩存文件一旦下載出錯,後續的文件將不再下載,拋出錯誤事件,見
-
Android 系統版本衆多,較低版本的瀏覽器對 manifest 支持不完善
-
引用 manifest 的 html 頁面本身也會被緩存
-
《慎用 manifest》一文提到的如:頁面的參數傳遞、manifest 的發佈、回滾、下線等問題
Localstorage
Localstorage
存儲的地方是跟 Web 緩存分開的,是瀏覽器重新開闢的一個地方。嚴格來講不屬於Web緩存
Localstorage 的作用
使 Web 頁面能夠通過瀏覽器提供的set/get
接口,存儲一些自定義的信息到本地硬盤,並且在單次訪問或以後的訪問過程中隨時獲取或修改。
Localstorage 的 API
setItem/getItem/removeItem/clear
Localstorage 的緩存用途
Localstorage 設計的本意可能是用來存儲一些用戶操作的個性化設置的文本類型的信息和數據,當我們其實也可能拿來當 Web 緩存區使用,比如我們可以將 Base64 格式編碼的圖片信息,存在 localstorage 中,再次訪問時,直接本地獲取後,使用 Css3 的 Data:image 的方式直接展現出來。
Localstorage 的存在的問題
大小限制,目前瀏覽器只給每個獨立的域名提供 5m 的存儲空間,當存儲超過 5m,瀏覽器就會彈出警告框。
Apache 緩存設置
對於 Apache 服務器,可以通過 mod_expires
模塊來設定Expires
或Cache-Control
HTTP 頭。編輯相應目錄下的 .htaccess
文件,或直接對 Apache 的配置文件(根據服務器系統版本不同,可能爲httpd.conf
或apache2.conf
等)作出修改。
分文件類別設定
使用 ExpiresByType 可以按照文件的 MIME Type 設定某一類文件的過期日期。例如:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 week"
ExpiresByType application/javascript "access plus 2 weeks"
ExpiresByType image/x-icon "access plus 6 months"
ExpiresByType image/gif "access plus 6 months"
ExpiresByType image/png "access plus 6 months"
ExpiresByType image/jpeg "access plus 6 months"
ExpiresByType video/x-flv "access plus 6 months"
ExpiresByType application/pdf "access plus 6 months"
</IfModule>
其中 access plus 1 week 表示將緩存過期設置爲訪問時間(即當前時間)之後的一週。如果將 access 替換爲 modification,則緩存過期會被設定爲文件修改時間之後的一週。
可以使用的時間單位包括:
years
months
weeks
days
hours
minutes
seconds
不同的時間也可以進行組合
ExpiresByType text/html "access plus 1 month 15 days 2 hours"
ExpiresByType image/gif "modification plus 5 hours 3 minutes"
根據文件擴展名進行設置
如果希望根據擴展名來指定緩存規則,可以使用 FilesMatch 配合正則表達式。爲了簡潔,我這裏只規定了 ExpiresDefault。它的優先級很低,只會在對應文件沒有任何其他規則能夠匹配(包括上層目錄下的緩存規則)時生效。
<IfModule mod_expires.c>
<FilesMatch "\.(css|js)$">
ExpiresActive on
ExpiresDefault "access plus 1 week"
</FilesMatch>
</IfModule>
對某些文件設定
<IfModule mod_expires.c>
<FilesMatch "^(example\.css|example\.js)$">
ExpiresActive on
ExpiresDefault "access plus 1 week"
</FilesMatch>
</IfModule>
對某一文件夾下的所有文件設定
對於靜態文件,一個比較方便的做法是將它們全部放到一個目錄下,並對該目錄下的所有文件設定。但是,此處需要注意防止其他規則將 ExpiresDefault 覆蓋掉。
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 10 years"
</IfModule>
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/lajDFcvjuWhrn0V8BpVTsg