初識http緩存

近日,發現我打包的js代碼上傳到服務器後,並無更新。想到用ng作了代理,多是ng緩存的問題,就查資料學習了一下http(1.1)緩存的東西。css

1.相關術語:(約定req爲請求頭,res響應頭,C客戶端,S服務端)

// response Headers
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 04 Dec 2019 09:11:07 GMT
Content-Type: text/css
Vary: Accept-Encoding
Last-Modified: Wed, 04 Dec 2019 09:03:18 GMT
ETag: W/"5de77656-2340"
Expires: Wed, 04 Dec 2019 21:11:06 GMT
Cache-Control: max-age=43200
Content-Encoding: gzip
複製代碼
  • Expires :res中爲資源過時時間
  • Last-Modified: res中爲資源最近修改時間
  • ETag: res中資源的惟一標識符(hash算法生成)
  • If-Modified-Since : req中的資源最近修改時間
  • If-None-Match :req中的資源標識
  • Cache-Control : resreq中表示緩存策略
  1. req中經常使用指令
字段名稱 說明
max-age= 設置緩存存儲的最大週期,超過這個時間緩存被認爲過時(單位秒)。與Expires相反,時間是相對於請求的時間
max-stale[=] C可接收一個已通過期的資源。設置一個可選的秒數,不接受超過給定時間的資源
min-fresh= C但願獲取一個能在指定的秒數內保持其最新狀態的res
no-cache 在發佈緩存副本以前,強制要求緩存把請求提交給原始服務器進行驗證
no-store 不緩存有關客戶端請求或服務器響應的任何內容
  1. res中經常使用指令(req中重複的不列舉,詳見MDN)
字段名稱 說明
public 代表響應能夠被任何對象(包括:發送請求的客戶端,代理服務器,等等)緩存,即便是一般不可緩存的內容
private 代表響應只能被單個用戶緩存,不能做爲共享緩存(即代理服務器不能緩存它)。私有緩存能夠緩存響應內容
must-revalidate 一旦資源過時(好比已經超過max-age),在成功向原始服務器驗證以前,緩存不能用該資源響應後續請求
proxy-revalidate must-revalidate做用相同,但它僅適用於共享緩存(例如代理),並被私有緩存忽略
s-maxage= 覆蓋max-age或者Expires頭,可是僅適用於共享緩存(好比各個代理),私有緩存會忽略它

其中Last-ModifiedIf-Modified-SinceETagIf-None-Match是在每次的res,req中配對使用的。html

2. 常見緩存場景:(約定資源爲app.js)

  1. Expires:
    • 過程vue

      1. C端請求app.js ----> S端。
      2. S端響應app.jsExpires ----> C端。(C端緩存app.js直到Expires)
      3. if 1 發生在Expires 前,會直接從緩存中取, else 2
      4. 重複以上
    • 優點nginx

      相比於最原始的不帶緩存的請求和相應,優點很明顯,會直接從緩存中取,減小請求響應次數git

    • 缺陷github

      若是app.jsExpires 內發生了改變,C端呈現的資源不是最新的。web

  2. Expires + Last-Modified:
    • 過程面試

      1. C端請求app.js ----> S
      2. S端響應app.jsExpiresLast-Modified ----> C端。(C端緩存app.js直到Expires,上次修改時間是 Last-Modified
        • if 1 發生在Expires 前,會直接從緩存中取(200)。
        • else C端請求S端,帶上If-Modified-Since(等於上一次相應的Last-Modified
        • S端用req中的If-Modified-Sinceres中的 Last-Modified比較。
          • if 一致,響應 C端:你能夠繼續用本地緩存(304)
          • else,2
      3. 重複以上
    • 優點算法

      相比與只使用Expires,if app.js發生變化,能夠更新緩存,C端呈現內容爲最新,else 不會有新的res拉一次資源,直接讀緩存vue-cli

    • 缺陷

      Last-Modified精確到秒,實際中有不少一秒內會完成不少reqres,問題呼之欲出

      • Last-Modified內,app.js被修改屢次,那麼C端仍是會從緩存中讀,呈現內容不是最新
      • 假設處於vue-cli開發下,由於某種緣由,代碼實際沒有修改,但CI/CD重複構建打包了文件,app.js變爲了app01.js(build.js生成的不一樣版本hash名稱),但代碼只是名稱變化,內容並不變化,卻從新拉了一次資源
  3. Expires + Last-Modified + ETag:
    • 過程

      1. C端請求app.js ----> S
      2. S端響應app.jsExpiresLast-Modified ,ETag----> C端。(C端緩存app.js直到Expires,上次修改時間是 Last-Modified,文件標識是ETag
        • if 1 發生在Expires 前,會直接從緩存中取(200)。
          • else C端請求S端,帶上If-Modified-Since(等於上一次相應的Last-Modified)和If-None-Match(等於上一次相應的Etag
            • S端用req中的If-None-Matchres中的 Etag比較,忽略If-Modified-SinceLast-Modified的比較。(若是Etag變化,Last-Modified必定變化,充分條件)
            • if 一致,響應 C端:你能夠繼續用本地緩存(304)
            • else,2
      3. 重複以上
    • 優點

      相較於上一種,使得資源變動的驗證更加嚴格。

    • 缺陷

      讓咱們設想這種狀況,咱們頻繁的修改app.js,打包構建,處於某種緣由,咱們並不想C端呈現最新的app.js,而是一段時間後再讀取最新的,顯然還達不到咱們的要求

  4. Expires + Last-Modified + ETag + Cache-Control :
    • 過程

      1. C端請求app.js ----> S
      2. S端響應app.jsExpiresLast-Modified ,ETagCache-Control:max-age=43200 ----> C端。(C端發現帶有 Cache-Control:max-age=43200,忽略Expires*,記住Last-Modified ,ETag
        • if 1 發生在(req發生的時間+ 12h(43200s)),會直接從緩存中取(200)。
          • else C端請求S端,帶上If-Modified-Since(等於上一次相應的Last-Modified)和If-None-Match(等於上一次相應的Etag
            • S端用req中的If-None-Matchres中的 Etag比較,忽略If-Modified-SinceLast-Modified的比較。(若是Etag變化,Last-Modified必定變化,充分條件)
            • if 一致,響應 C端:你能夠繼續用本地緩存(304)
            • else,2
      3. 重複以上
    • 優點

      達到了咱們上個方案達不到的效果

    • 缺陷?

      C端沒法主動知道S端上咱們請求的資源變化,只能被動的從res中得知,這算缺陷嗎?

3. 常見問題:(約定資源爲app.js)

  1. 如何設置不緩存?

    • ng配置以下:

      // 還有多種設置方法,舉例一種
      // 重啓ng不必定當即生效
      location / {
          access_log /data/nginx/log/xxx.log api;
          
          root /home/www/html;
          
          if ($request_filename ~ .*\.(htm|html)$)
          
           {
                  add_header Cache-Control no-cache;
           }
      }
      複製代碼
    • 打包html設置meta標籤以下

      <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
      <meta http-equiv="Pragma" content="no-cache" />
      <meta http-equiv="Expires" content="0" />
      複製代碼

      meta是用來在HTML文檔中模擬HTTP協議的響應頭報文。meta 標籤用於網頁的與中,meta 標籤的用處不少。meta 的屬性有兩種:name和http-equiv。name屬性主要用於描述網頁,對應於content(網頁內容),以便於搜索引擎機器人查找、分類(目前幾乎全部的搜索引擎都使用網上機器人自動查找meta值來給網頁分類)。這其中最重要的是description(站點在搜索引擎上的描述)和keywords(分類關鍵詞),因此應該給每頁加一個meta值。

  2. 如何清理緩存?

    • Nginx企業版提供了purger功能,對於社區版Nginx能夠考慮使用ngx_cache_purge(該方法最好限制其訪問權限,如只容許內網能夠訪問或者須要密碼才能訪問)github.com/FRiCKLE/ngx…

      location ~ /purge(/.*) {
      
        allow 127.0.0.1;
      
        deny all;
      
        proxy_cache_purge cache$1$is_args$args;
      
       }
      複製代碼

      PS: 相似寶塔面板這種Ng都自動安裝了ngx_cache_purge模塊,如何設置詳見下方參考。

    • 找到緩存文件夾,直接kill。

  3. 若是發生緩存錯誤,檢查的步驟?

    1. 檢查是否傳錯文件夾
      • 打開項目打包後的js,檢查app.js文件名。
      • 打開瀏覽器控制檯Network,勾選js,F5刷新後找到對應的app.xxx.js,比較。若是你發現名稱不同,並且res頭部 Last-Modified也不對,那麼大機率你傳錯文件夾了。
    2. 檢查是否正確更新
      • 記錄現階段 ETag
      • 從新上傳後刷新,比較兩次 ETag是否一致
    3. 檢查是否正確配置ng等Server
    4. 梳理構建部署步驟,逐步檢查(只能幫你到這啦)

只是http(1.1)的部分常見場景,目前到這裏已經足夠,咱得一步一步來,切勿囫圇吞棗~

思考有限,不免出現疏漏,歡迎諸位指出,集思廣益。

歡迎關注個人公衆號《web工程師的自我修養》,一塊兒交流學習共勉~

reference
  1. 《面試精選之http緩存》 juejin.im/post/5b3c87…
  2. 《MDN - Cache-Control》developer.mozilla.org/zh-CN/docs/…
  3. 《淺談http中的Cache-Control》blog.csdn.net/u012375924/…
  4. 《Nginx緩存配置及nginx ngx_cache_purge模塊的使用》www.cnblogs.com/Eivll0m/p/4…
相關文章
相關標籤/搜索