詳解HTTP緩存

来源:https://www.cnblogs.com/isLiu/archive/2018/01/23/8335235.html
-Advertisement-
Play Games

HTTP緩存是個大公司面試幾乎必考的問題,寫篇隨筆說一下HTTP緩存。 1. HTTP報文首部中有關緩存的欄位 在HTTP報文中,與緩存相關的信息都存在首部里,簡單說一下首部。 首部 HTTP首部欄位向請求報文和相應報文中添加了一些附加信息。本質上來說,它們只是一些鍵值對的列表。比如,下麵的首部行會 ...


HTTP緩存是個大公司面試幾乎必考的問題,寫篇隨筆說一下HTTP緩存。

1. HTTP報文首部中有關緩存的欄位


在HTTP報文中,與緩存相關的信息都存在首部里,簡單說一下首部。

首部

HTTP首部欄位向請求報文和相應報文中添加了一些附加信息。本質上來說,它們只是一些鍵值對的列表。比如,下麵的首部行會向Content-Length首部欄位賦值19:

Content-Length: 19

HTTP規範定義了幾中首部欄位。應用程式也可以隨意發明自己所用的首部。HTTP首部可以分為以下幾類:

  • 通用首部

    既可以出現在請求報文中,也可以出現在響應報文中。

  • 請求首部

    提供更多有關請求的信息。

  • 響應首部

    提供更多有關響應的信息。

  • 實體首部

    描述主體的長度和內容,或資源本身。

  • 擴展首部

    規範中沒有定義的新首部。

想瞭解更多有關HTTP首部或報文的信息,個人推薦《HTTP權威指南》。

首部中與緩存有關的欄位

  • 通用首部欄位

  • 請求首部欄位

  • 響應首部欄位

  • 實體首部欄位

2. 緩存的處理步驟


除了一些微小的細節,Web緩存的工作原理基本很簡單,對一條HTTP GET報文的基本緩存處理過程包括7個步驟。

  1. 接收——緩存從網路中讀取抵達的請求報文。
  2. 解析——緩存對報文解析,提取出URL和各種首部。
  3. 查詢——緩存查看是否有本地副本可用,如果沒有就向伺服器獲取一份副本,並將其保存在本地。
  4. 新鮮度檢測——緩存查看以緩存的副本是否新鮮,如果不是,就詢問伺服器是否有更新。
  5. 創建響應——緩存會用新的首部和以緩存的主體來構建一條響應報文。
  6. 發送——緩存通過網路將響應發揮給客戶端。
  7. 日誌——緩存可選的創建一個日誌文件來描述這個事務。

緩存的處理步驟如圖:

3. 文檔過期和伺服器再驗證


文檔過期

通過特殊的HTTP Cache-Control首部和Expires首部,HTTP讓原始伺服器向每個文檔加了一個過期時間,這些首部說明瞭多長時間內可將這些內容視為新鮮的。

在文檔過期之前,緩存可以以任意頻率使用這些副本,而無需與伺服器進行聯繫,除非客戶端請求中包含有阻止提供已緩存或未驗證資源的首部。一旦已緩存的文檔過期,緩存就必須與伺服器進行核對,詢問文檔是否被修改過,如果被修改過,就要獲取一份新鮮的並帶有新的過期日期的副本。

伺服器再驗證

但是緩存文檔過期並不意味著它與伺服器上的文檔有實際的區別,只是以為到了要進行核對的時間了。這種情況叫伺服器再驗證,說明緩存需要詢問伺服器文檔是否發生了變化。

  • 如果再驗證顯示內容發生了變化,緩存會獲取一份新的文檔副本,並將其存儲在舊文檔的位置上,然後將文檔發送給客戶端。

  • 如果再驗證顯示內容沒有發生變化,緩存只要獲取新的首部,包括一個新的過期日期,並對緩存中的首部進行更新就好了。

HTTP定義了幾個首部用來實現緩存是否新鮮的驗證,像開篇我們說到的If-Modified-Since和If-None-Match、Last-Modified等都屬於這樣的首部。

強緩存和協商緩存

我們根據是否需要向伺服器發起請求將緩存分成兩類,不需要向伺服器發起請求的緩存叫強緩存,也就是上面所說的文檔沒過期時候用到的緩存。需要向伺服器發起請求的緩存叫協商緩存,也就是上面伺服器再驗證用到的緩存。

下麵我們詳細介紹強緩存和協商緩存。

4. 強緩存


上面我們說可以通過Cache-Control首部和Expires首部來標明文檔的過期時間。如果沒有過期的話,自然就是從緩存里取文檔了:

顧名思義,這裡的memory cache記憶體了,disk cache就是磁碟的緩存了,再往下說就到了webkit的緩存機制了。

Expires

為什麼要先說Expires呢?因為相比於Cache-Control,Expires出現的較早,是HTTP1.0的東西,而Cache-Control是HTTP1.1的東西。

Expires的值對應一個GMT,也就是格林尼治時間,比如“Mon, 22 Jan 2019 11:12:01 GMT”來告訴瀏覽器資源緩存過期時間,如果還沒過該時間點則不發請求。

在客戶端我們同樣可以使用meta標簽來知會IE(也僅有IE能識別)頁面(同樣也只對頁面有效,對頁面上的資源無效)緩存時間:

<meta http-equiv="expires" content="mon, 18 apr 2016 14:30:00 GMT">

如果希望在IE下頁面不走緩存,希望每次刷新頁面都能發新請求,那麼可以把“content”里的值寫為“-1”或“0”。但是是該方式僅僅作為知會IE緩存時間的標記,你並不能在請求或響應報文中找到Expires欄位。

那麼如果Pragma和Expires一起出現的話,Pragma的優先順序是高的。

註意:響應報文中Expires所定義的緩存時間是相對伺服器上的時間而言的,如果客戶端上的時間跟伺服器上的時間不一致,特別是如果你修改了自己電腦的系統時間,那緩存時間可能就沒什麼意義了。

Pragma

既然我們已經說了Expires是HTTP1.0的遺留物,那我們也要介紹下Pragma。

當該欄位值為“no-cache”的時候,會通知客戶端不要對該資源讀緩存,即每次都得向伺服器發一次請求才行。

Pragma屬於通用首部欄位,在客戶端上使用時,常規要求我們往html上加上這段meta元標簽:

<meta http-equiv="Pragma" content="no-cache">

它告訴瀏覽器每次請求頁面時都不要讀緩存,都得往伺服器發一次請求才行。

但是這種禁用緩存的形式作用不是那麼太大:

  1. 僅有IE才能識別這段meta標簽含義,其它主流瀏覽器僅能識別“Cache-Control: no-store”的meta標簽。

  2. 在IE中識別到該meta標簽含義,並不一定會在請求欄位加上Pragma,但的確會讓當前頁面每次都發新請求,但是僅限頁面,頁面上的資源則不受影響。

所以這種在客戶端定義Pragma並沒有多少作用。

但是在響應報文中加上這個欄位就不一樣了,瀏覽器在收到伺服器的Pragma欄位後會對資源進行標記,禁用其緩存行為,進而後續每次刷新頁面均能重新發出請求而不走緩存。

Cache-Control

針對上述的“Expires時間是相對伺服器而言,無法保證和客戶端時間統一”的問題,HTTP1.1新增了 Cache-Control 來定義緩存過期時間,若報文中同時出現了 Pragma、Expires 和 Cache-Control,會以 Cache-Control 為準。

Cache-Control也是一個通用首部欄位,這意味著它能分別在請求報文和響應報文中使用。在RFC中規範了 Cache-Control 的格式為:

"Cache-Control" ":" cache-directive"

作為請求首部時,cache-directive的可選值有:

作為響應首部時,cache-directive的可選值有:

我們依舊可以在HTML頁面加上meta標簽來給請求報頭加上 Cache-Control欄位,並且可以有多個值:

Cache-Control: max-age=3600, must-revalidate

它意味著該資源是從原伺服器上取得的,且其新鮮度的有效時間為一小時,在後續一小時內,用戶重新訪問該資源則無鬚髮送請求。

當然這種組合的方式也會有些限制,比如 no-cache 就不能和 max-age、min-fresh、max-stale 一起搭配使用。

組合的形式還能做一些瀏覽器行為不一致的相容處理。例如在IE我們可以使用 no-cache 來防止點擊“後退”按鈕時頁面資源從緩存載入,但在 Firefox 中,需要使用 no-store 才能防止歷史回退時瀏覽器不從緩存中去讀取數據,故我們在響應報頭加上如下組合值即可做相容處理:

Cache-Control: no-cache, no-store

5. 協商緩存


顧名思義,客戶端通過與伺服器進行協商是否使用緩存。前面我們已經說過了HTTP提供了實現緩存文件是否新鮮的驗證、提升緩存的復用率的幾個首部,就來說說這些首部。其實它們都是HTTP1.1新增的。

Last-Modified

伺服器將資源傳遞給客戶端時,會將資源最後更改的時間以“Last-Modified: GMT”的形式加在實體首部上一起返回給客戶端。

客戶端會為資源標記上該信息,下次再次請求時,會把該信息附帶在請求報文中一併帶給伺服器去做檢查,若傳遞的時間值與伺服器上該資源最終修改時間是一致的,則說明該資源沒有被修改過,直接返回304狀態碼即可。

至於傳遞標記起來的最終修改時間的請求報文首部欄位一共有兩個:

⑴ If-Modified-Since: Last-Modified-value

示例:

If-Modified-Since: Thu, 31 Mar 2016 07:07:52 GMT

該請求首部告訴伺服器如果客戶端傳來的最後修改時間與伺服器上的一致,則直接回送304 和響應報頭即可。

當前各瀏覽器均是使用的該請求首部來向伺服器傳遞保存的 Last-Modified 值。

⑵ If-Unmodified-Since: Last-Modified-value

告訴伺服器,若Last-Modified沒有匹配上,即資源在服務端的最後更新時間改變了,則應當返回412(Precondition Failed) 狀態碼給客戶端。

當遇到下麵情況時,If-Unmodified-Since 欄位會被忽略:

  1. Last-Modified值相等,即資源在服務端沒有新的修改;

  2. 服務端需返回2XX和412之外的狀態碼;

  3. 傳來的指定日期不合法

Last-Modified 說好卻也不是特別好,因為如果在伺服器上,一個資源被修改了,但其實際內容根本沒發生改變,會因為Last-Modified時間匹配不上而返回了整個實體給客戶端。

ETag

為瞭解決上述Last-Modified可能存在的不准確的問題,Http1.1還推出了 ETag 實體首部欄位。

伺服器會通過某種演算法,給資源計算得出一個唯一標誌符(比如md5標誌),在把資源響應給客戶端的時候,會在實體首部加上“ETag: 唯一標識符”一起返回給客戶端。

客戶端會保留該 ETag 欄位,併在下一次請求時將其一併帶過去給伺服器。伺服器只需要比較客戶端傳來的ETag跟自己伺服器上該資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。

如果伺服器發現ETag匹配不上,那麼直接以常規GET 200回包形式將新的資源以及新的Etag發給客戶端;如果ETag是一致的,則直接返回304知會客戶端直接使用本地緩存即可。

那麼客戶端是如何把標記在資源上的 ETag 傳去給伺服器的呢?請求報文中有兩個首部欄位可以帶上 ETag 值:

⑴ If-None-Match: ETag-value

示例為

If-None-Match: "56fcccc8-1699"

告訴服務端如果 ETag 沒匹配上需要重發資源數據,否則直接回送304 和響應報頭即可。

當前各瀏覽器均是使用的該請求首部來向伺服器傳遞保存的 ETag 值。

⑵ If-Match: ETag-value

告訴伺服器如果沒有匹配到ETag,或者收到了“*”值而當前並沒有該資源實體,則應當返回412(Precondition Failed) 狀態碼給客戶端。否則伺服器直接忽略該欄位。

If-Match 的一個應用場景是,客戶端走PUT方法向服務端請求上傳/更替資源,這時候可以通過 If-Match 傳遞資源的ETag。

需要註意的是,如果資源是走分散式伺服器(比如CDN)存儲的情況,需要這些伺服器上計算ETag唯一值的演算法保持一致,才不會導致明明同一個文件,在伺服器A和伺服器B上生成的ETag卻不一樣。

如果 Last-Modified 和 ETag 同時被使用,則要求它們的驗證都必須通過才會返回304,若其中某個驗證沒通過,則伺服器會按常規返回資源實體及200狀態碼。

如果面試的時候能說出這些,就代表了你對HTTP緩存理解的很不錯了,如果是一百分的話也應該可以拿到八十分了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 2017年npm上最熱門的項目之一——Pkg,可以直接將node.js項目打包成windows可以直接執行的exe文件(也支持FreeBSD、linux、macos、arm系統),並且無須修改你項目中的任何代碼。本文對該項目作了簡要說明與教程,並提供了一個簡單示例來說明其使用場景。 ...
  • CreateJS介紹請看CreateJS中文網或者官方網站 一.使用EaselJS 先到官網下載easeljs,也可以點這裡 1.得到easeljs-0.8.1.min.js後,新建一個HTML5文件並導入: 2.入口函數和創建canvas標簽: 3.創建舞臺: 二.使用EaselJS創建圖形和文字 ...
  • css代碼: html代碼: JS調用: ...
  • jquery的綠色拖動驗證功能 在網上看到了一個這樣的問題:那種像拖動滑塊匹配圖形的驗證方式是怎麼實現的?。 突然想到實現一個簡單綠色拖動驗證碼的功能,在網上搜了下,有一個用jquery實現的該功能代碼。 體驗地址:http://yanshi.sucaihuo.com/jquery/8/897/de ...
  • 最近在做的一個後臺管理系統,發現一個bug: 問題描述:如果其他列表項都用類為col-xs-12的div包裹,而引入UEditor的部分不用類為col-xs-12的div包裹,那麼其他列表項將無法顯示,UEditor編輯框也顯示不完全; 解決辦法:將引入UEditor的部分也用類為col-xs-12 ...
  • Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。 在 Vue 之後引入 vuex 會進行自動安裝: 可以通過 https://unpkg.com/[email protected] 這樣的方式指定特定的版本 ...
  • 其實json格式化沒想象中的那麼複雜,難點就是json格式化的工作流程。 正好工作上需要,於是就搞了一套json格式化+json著色的方法,原生的方法,可以直接使用。json數據格式化前後對比圖,如下: 下麵是源碼,可以根據個人需求適當修改: ...
  • 在做後臺管理界面的時候,幾乎少不了的一個結構就是樹形結構,用來做菜單導航; 那麼,最希望的就是樹結構的所有數據都是讀取的資料庫,而不是直接代碼當中寫死,那我們就一步一步來看: 一,建表 欄位通常包括:id,text,url,pid 二,使用nodeJS連接資料庫 三,讀取數據 四,把讀過來的數據轉成 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...