1. 程式人生 > >HTTP協議探究(一)

HTTP協議探究(一)

cati disk 私有 bubuko 改變 form sha 決定 本地

一 復習與目標

1 復習

  • 序章主要用WrieShark抓包HTTP報文
  • 復習了TCP協議
  • 講述了TCP協議與HTTP之間的關系
  • HTTP1.1更新原因:HTTP1.0一次TCP連接只能發送一次HTTP報文等
  • HTTP2.0更新原因:HTTP的報頭太大、多路復用問題等(HTTP2.0未來研究)

2 目標

  • 由於大家都有一定的基礎(包括我),所以並不會照著書本一節一節地進行,所以這一節重點講一下緩存相關的問題。
  • 緩存的好處
  • 緩存相關的狀態碼
  • 緩存相關的首部
  • 緩存的處理步驟

二 為什麽要有緩存?

  • 減少冗余的數據傳輸
  • 緩解網絡瓶頸
  • 降低對源服務器的要求
  • 降低距離時延

註:其實所有的好處都是不去重復獲取相同文件帶來的。

三 緩存存放在哪裏?

  • 代理服務器(如:Nginx)
  • 瀏覽器(如:Chrome)

註:由於現在一般前後端分離開發,如:前端用Angular(Nginx),後端用Java(Tomcat),前端打包構建(代碼壓縮 編譯優化 代碼混淆等操作)成靜態文件存放在Nginx中。本文主要以Chrome與Nginx的交互做例子。

四 緩存相關狀態碼

  • 200:請求成功
  • 304:請求資源服務器,返回資源未改動
  • 200:過期時間內,瀏覽器直接獲取硬盤內的緩存數據(from disk cache)
  • 304:過期時間內,瀏覽器直接獲取內存內的緩存數據(from memory cache)

五 緩存相關的HTTP首部

1 驗證相關

(1)Etag示例

# 以Nginx生成的Etag值為例
# etag == last-modified的秒級Unix時間戳(16進制) - content-length(16進制)

# 響應首部
last-modified: Fri, 23 Nov 2018 03:47:36 GMT
content-length: 1408
etag: W/"5bf77858-580"

# 請求首部
if-none-match: W/"5bf77858-580"
if-modified-since: Fri, 23 Nov 2018 03:47:36 GMT
  • Nginx收到請求後,比較Etag值是否修改,修改則返回新文件,狀態碼為200。
  • 否則,返回狀態嗎304,告知瀏覽器從硬盤獲取即可。

(2)強弱驗證器

  • 弱驗證器:服務器對文檔進行一些非實質性或不重要的修改時,不希望已緩存的副本都失效。Etag表達:"5bf77858-580"。
  • 強驗證器:文檔進行任何修改都會使得緩存的副本失效。Etag表達:W/"5bf77858-580"。

2 Cache-Control

(1)響應請求角度

  • 緩存請求指令
指令 參數 說明
no-cache 強制向源服務器再次驗證(返回200或者304)
no-store 不緩存請求或者響應的任何內容(返回200)
max-age=[s] 必須 響應的最大Age值
max-stale(=[s]) 可省略 過期也照常接收
min-fresh=[s] 必須 要求緩存服務器返回至少還未過指定時間的緩存資源
no-transform 代理不可更改媒體類型
only-if-cached 要求緩存服務器不重新加載響應,也不會再次確認資源有效性
  • 緩存響應指令
指令 參數 說明
public 向任意方(代理或瀏覽器)提供響應的緩存
private 可省略 僅向特定用戶(瀏覽器)返回響應
no-cache 強制向源服務器再次驗證(返回200或者304)
no-store 不緩存請求或者響應的任何內容(返回200)
no-transform 代理不可更改媒體類型
must-revalidate 代理可緩存但是必須再向源服務器進行確認(忽略請求的max-stale)
proxy-revalidate 所有緩存服務器在接收到客戶端帶有該指令的請求返回響應之前,必須再次驗證緩存的有效性
max-age=[s] 可省略 響應的最大Age值(忽略請求的Expires)
s-maxage = [ 秒] 必需 公共緩存服務器響應的最大Age值

(2)功能角度

  • 什麽是可以緩存的?響應(源服務器)決定
    • public(共享緩存):響應消息可被任何緩存保存
    • private(私有緩存):響應消息部分或者全部可被某個用戶(如:瀏覽器)保存,但不可被共享緩存保存。
    • no-cache(不緩存):指定一個或多個field-name不可緩存,即每次都需要去驗證
  • 什麽能被緩存保存?響應決定
    • no-store(不保存):防止敏感信息泄露,整個響應消息都不能保存。
  • 對基本過期機制改進?響應或者請求決定
    • Expires:指定過期時間。
    • s-maxage(針對代理服務器):對共享(緩存來說,s-maxage指定的值將會覆蓋max-age緩存控制指令或Expires頭域
    • max-age(針對用戶終端):優先級高於Expires,max-age未到時,不會去源服務器請求,而是從本地硬盤或內存中獲取。
    • min-fresh(保鮮時間):客戶端想要響應至少在保鮮時間(min-fresh)內。
    • max-stale(過期時間):客戶端想要響應在保鮮時間內或過期不超過max-stale;若max-stale沒有賦值,則客戶端願接受任意年齡的陳舊響應。
  • 緩存重驗證和加載控制?用戶代理(響應或者請求)決定
    • max-age=0:強迫重驗證它所擁有的緩存項。
    • only-if-cache:糟糕的網絡連接下,客戶端希望緩存只返回緩存當前保存的響應,並且不需要通過源服務器對其緩存項進行重新加載或重驗證。
    • must-revalidate(針對代理服務器):如果基於源服務器的Expire或max-age值,已緩存的響應超過max-age時間,緩存必須每次重驗證。
    • proxy-revalidate(針對用戶終端):類似於must-revalidate,但不適用於代理緩存。

(3)組合使用註意

  • Cache-Control:max-age,s-maxage;時,那麽代理使用s-maxage進行緩存,瀏覽器使用max-age進行緩存。
  • Cache-Control:no-cache,max-age=60;時,max-age失效。
  • Cache-Control:no-cache,no-store;時,no-cache失效。
  • revalidate能與max-age能夠配合使用,即max-age沒過期,使用本地;過期則去重新獲取。
  • 單獨使用revalidate時,瀏覽器或代理會有默認的max-age,不同的瀏覽器該值不一樣(所以禁止這樣使用)。
  • 一定要指定private public來指定允許緩存的代理或瀏覽器

(4)示例

  • 服務器:Nginx
  • 客戶端:Chrome
# demo1
location / {
    add_header Cache-Control max-age=30;
    root   /home/nginx/html;
    index  index.html;
}

# demo2
location / {
    add_header Cache-Control private,max-age=30,proxy-revalidate;
    root   /home/nginx/html;
    index  index.html;
}
  • 執行結果是一樣的,max-age時間內,從本地獲取,超出則訪問服務器。
  • 但是我強烈建議使用第二種,因為Chrome中兩個的效果是相同的,鬼知道其他的瀏覽器會是怎樣的體現。

六 廢棄和更新緩存的響應

技術分享圖片

  • HTML 被標記為“no-cache”,這意味著瀏覽器在每次請求時都始終會重新驗證文檔,並在內容變化時獲取最新版本。此外,在 HTML 標記內,您在 CSS 和 JavaScript 資產的網址中嵌入指紋:如果這些文件的內容發生變化,網頁的 HTML 也會隨之改變,並會下載 HTML 響應的新副本。
  • 允許瀏覽器和中間緩存(例如 CDN)緩存 CSS,並將 CSS 設置為 1 年後到期。請註意,您可以放心地使用 1 年的“遠期過期”,因為您在文件名中嵌入了文件的指紋:CSS 更新時網址也會隨之變化。
  • JavaScript 同樣設置為 1 年後到期,但標記為 private,這或許是因為它包含的某些用戶私人數據是 CDN 不應緩存的。
  • 圖像緩存時不包含版本或唯一指紋,並設置為 1 天後到期。

註:一般打包工具都具備給文件嵌入指紋,整個不需要擔心。

如:Angualr項目打包生成項目結構

index.html
assets
    img
        logo.png
        ...
1.4f4fe517e78bb7b8a11d.js # 組件混淆文件
styles.2c73b882414fbdd03a9f.css
....

七 緩存檢查清單

  • 使用一致的網址:如果您在不同的網址上提供相同的內容,將會多次獲取和存儲這些內容。
  • 確保服務器提供驗證令牌 (ETag):有了驗證令牌,當服務器上的資源未發生變化時,就不需要傳送相同的字節。(Nginx自帶Etag計算)
  • 確定中間緩存可以緩存哪些資源:對所有用戶的響應完全相同的資源非常適合由 CDN 以及其他中間緩存進行緩存。(Nginx進行定制)
  • 為每個資源確定最佳緩存周期:不同的資源可能有不同的更新要求。為每個資源審核並確定合適的 max-age。(Nginx進行定制)
  • 確定最適合您的網站的緩存層次結構:您可以通過為 HTML 文檔組合使用包含內容指紋的資源網址和短時間或 no-cache 周期,來控制客戶端獲取更新的速度。(Angular打包自動支持)
  • 最大限度減少攪動:某些資源的更新比其他資源頻繁。如果資源的特定部分會經常更新,可以考慮將其代碼作為單獨的文件提供。這樣一來,每次獲取更新時,其余內容可以從緩存獲取,從而最大限度減少下載的內容大小。

參考:

  • https://www.cnblogs.com/chyingp/p/no-cache-vs-must-revalidate.html
  • https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn
  • https://www.web-tinker.com/article/21217.html
  • https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
  • 《HTTP權威指南》第7章
  • 《圖解HTTP》第6章

HTTP協議探究(一)