HTTP緩存機制及原理

首先了解HTTP報文

HTTP報文就是瀏覽器和服務器間通信時發(fā)送及響應的數據塊。
瀏覽器向服務器請求數據,發(fā)送請求(request)報文;服務器向瀏覽器返回數據,返回響應(response)報文。
報文信息主要分為兩部分
1.包含屬性的首部(header)--------------------------附加信息(cookie,緩存信息等)與緩存相關的規(guī)則信息,均包含在header中
2.包含數據的主體部分(body)-----------------------HTTP請求真正想要傳輸的部分

接下來的內容中我們將通過以下幾個部分來探討瀏覽器緩存機制:

  • 緩存位置

  • 緩存策略

  • 實際場景應用緩存策略

緩存位置

從緩存位置上來說分為四種,并且各自有優(yōu)先級,當依次查找緩存且都沒有命中的時候,才會去請求網絡.

1.Service Worker
2.Memory Cache
3.Disk Cache
4.Push Cache
5.網絡請求

Service Worker

Service Worker 的緩存與瀏覽器其他內建的緩存機制不同,它可以讓我們自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存,并且緩存是持續(xù)性的。

當 Service Worker 沒有命中緩存的時候,我們需要去調用 fetch 函數獲取數據。也就是說,如果我們沒有在 Service Worker 命中緩存的話,會根據緩存查找優(yōu)先級去查找數據。但是不管我們是從 Memory Cache 中還是從網絡請求中獲取的數據,瀏覽器都會顯示我們是從 Service Worker 中獲取的內容。

Memory Cache

Memory Cache 也就是內存中的緩存,讀取內存中的數據肯定比磁盤快。但是內存緩存雖然讀取高效,可是緩存持續(xù)性很短,會隨著進程的釋放而釋放。 一旦我們關閉 Tab 頁面,內存中的緩存也就被釋放了。

當我們訪問過頁面以后,再次刷新頁面,可以發(fā)現很多數據都來自于內存緩存.


從內存中讀取緩存

那么既然內存緩存這么高效,我們是不是能讓數據都存放在內存中呢?

先說結論,這是不可能的。首先計算機中的內存一定比硬盤容量小得多,操作系統需要精打細算內存的使用,所以能讓我們使用的內存必然不多。內存中其實可以存儲大部分的文件,比如說 JSS、HTML、CSS、圖片等等。但是瀏覽器會把哪些文件丟進內存這個過程就很玄學了,我查閱了很多資料都沒有一個定論。

當然,我通過一些實踐和猜測也得出了一些結論:

  • 對于大文件來說,大概率是不存儲在內存中的,反之優(yōu)先
  • 當前系統內存使用率高的話,文件優(yōu)先存儲進硬盤

Disk Cache

Disk Cache 也就是存儲在硬盤中的緩存,讀取速度慢點,但是什么都能存儲到磁盤中,比之 Memory Cache 勝在容量和存儲時效性上。

在所有瀏覽器緩存中,Disk Cache 覆蓋面基本是最大的。它會根據 HTTP Herder 中的字段判斷哪些資源需要緩存,哪些資源可以不請求直接使用,哪些資源已經過期需要重新請求。并且即使在跨站點的情況下,相同地址的資源一旦被硬盤緩存下來,就不會再次去請求數據。

Push Cache

Push Cache 是 HTTP/2 中的內容,當以上三種緩存都沒有命中時,它才會被使用。并且緩存時間也很短暫,只在會話(Session)中存在,一旦會話結束就被釋放。

Push Cache 在國內能夠查到的資料很少,也是因為 HTTP/2 在國內不夠普及,但是 HTTP/2 將會是日后的一個趨勢。這里推薦閱讀 HTTP/2 push is tougher than I thought 這篇文章,但是內容是英文的,我翻譯一下文章中的幾個結論,有能力的同學還是推薦自己閱讀

  • 所有的資源都能被推送,但是 Edge 和 Safari 瀏覽器兼容性不怎么好
  • 可以推送 no-cache 和 no-store 的資源
  • 一旦連接被關閉,Push Cache 就被釋放
  • 多個頁面可以使用相同的 HTTP/2 連接,也就是說能使用同樣的緩存
  • Push Cache 中的緩存只能被使用一次
  • 瀏覽器可以拒絕接受已經存在的資源推送
  • 你可以給其他域名推送資源

網絡請求

如果所有緩存都沒有命中的話,那么只能發(fā)起請求來獲取資源了。

那么為了性能上的考慮,大部分的接口都應該選擇好緩存策略,接下來我們就來學習緩存策略這部分的內容。

緩存策略

強緩存

用戶發(fā)送的請求,直接從客戶端緩存中獲取,不發(fā)送請求到服務器,不與服務器發(fā)生交互行為。

協商緩存(對比緩存)

用戶發(fā)送的請求,發(fā)送到服務器后,由服務器判定是否從緩存中獲取資源。

兩者共同點:客戶端獲得的數據最后都是從客戶端緩存中獲得。

兩者的區(qū)別:從名字就可以看出,強緩存不與服務器交互(除第一次),而協商緩存則需要與服務器交互。

細說強緩存


從上文我們得知,強制緩存,在緩存數據未失效的情況下,可以直接使用緩存數據,那么瀏覽器是如何判斷緩存數據是否失效呢?
我們知道,在沒有緩存數據的時候,瀏覽器向服務器請求數據時,服務器會將數據和緩存規(guī)則一并返回,緩存規(guī)則信息包含在響應header中。對于強制緩存來說,響應header中會有兩個字段來標明失效規(guī)則(Expires/Cache-Control)使用chrome的開發(fā)者工具,可以很明顯的看到對于強制緩存生效時,網絡請求的情況。

Cache-Control
Cache-Control 是最重要的規(guī)則。常見的取值有private、public、no-cache、max-age,no-store,默認為private。
private: 客戶端可以緩存
public: 客戶端和代理服務器都可緩存(前端的同學,可以認為public和private是一樣的)
max-age=xxx: 緩存的內容將在 xxx 秒后失效
no-cache: 指定不緩存響應,表明資源不進行緩存。但是設置了no-cache之后并不代表瀏覽器不緩存,而是在緩存前要向服務器確認資源是否被更改。因此有的時候只設置no-cache防止緩存還是不夠保險,還可以加上private指令,將過期時間設為過去的時間.
s-maxage: 只用于共享緩存,比如CDN緩存(s -> share)。與max-age 的區(qū)別是:max-age用于普通緩存,
而s-maxage用于代理緩存。如果存在s-maxage,則會覆蓋max-age 和 Expires.
no-store: 所有內容都不會緩存,強制緩存,協商緩存都不會觸發(fā)


圖中Cache-Control僅指定了max-age,所以默認為private,緩存時間為31536000秒(365天)
也就是說,在365天內再次請求這條數據,都會直接獲取緩存數據庫中的數據,直接使用。
1) 查看是否有cache-control 的max-age / s-maxage , 如果有,則用服務器時間date值 + max-age/s-maxage 的秒數計算出新的過期時間,將當前時間與過期時間進行比較,判斷是否過期
2)查看是否有cache-control 的max-age / s-maxage,則用expires 作為過期時間比較

所以判斷緩存是否過期步驟是:
1) 查看是否有cache-control 的max-age / s-maxage , 如果有,則用服務器時間date值 + max-age/s-maxage 的秒數計算出新的過期時間,將當前時間與過期時間進行比較,判斷是否過期
2)查看是否有cache-control 的max-age / s-maxage,則用expires 作為過期時間比較

細說協商緩存(對比緩存)


協商緩存,顧名思義,需要進行比較判斷是否可以使用緩存。
瀏覽器第一次請求數據時,服務器會將緩存標識與數據一起返回給客戶端,客戶端將二者備份至緩存數據庫中。
再次請求數據時,客戶端將備份的緩存標識發(fā)送給服務器,服務器根據緩存標識進行判斷,判斷成功后,返回304狀態(tài)碼,通知客戶端比較成功,可以使用緩存數據。

第一次訪問:



第二次訪問



通過兩圖的對比,我們可以很清楚的發(fā)現,在對比緩存生效時,狀態(tài)碼為304,并且報文大小和請求時間大大減少。
原因是,服務端在進行標識比較后,只返回header部分,通過狀態(tài)碼通知客戶端使用緩存,不再需要將報文主體部分返回給客戶端。

對于對比緩存來說,緩存標識的傳遞是我們著重需要理解的,它在請求header和響應header間進行傳遞,一共分為兩種標識傳遞,接下來,我們分開介紹。

Last-Modified / If-Modified-Since

Last-Modified:服務器在響應請求時,告訴瀏覽器資源的最后修改時間。



If-Modified-Since:
再次請求服務器時,通過此字段通知服務器上次請求時,服務器返回的資源最后修改時間。
服務器收到請求后發(fā)現有頭If-Modified-Since 則與被請求資源的最后修改時間進行比對。
若資源的最后修改時間大于If-Modified-Since,說明資源又被改動過,則響應整片資源內容,返回狀態(tài)碼200;
若資源的最后修改時間小于或等于If-Modified-Since,說明資源無新修改,則響應HTTP 304,告知瀏覽器繼續(xù)使用所保存的cache。



================================================================================

Etag / If-None-Match(優(yōu)先級高于Last-Modified / If-Modified-Since)

Etag:
服務器響應請求時,告訴瀏覽器當前資源在服務器的唯一標識(生成規(guī)則由服務器決定)。



If-None-Match:
再次請求服務器時,通過此字段通知服務器客戶段緩存數據的唯一標識。
服務器收到請求后發(fā)現有頭If-None-Match 則與被請求資源的唯一標識進行比對,
不同,說明資源又被改動過,則響應整片資源內容,返回狀態(tài)碼200;
相同,說明資源無新修改,則響應HTTP 304,告知瀏覽器繼續(xù)使用所保存的cache。



總結
對于強制緩存,服務器通知瀏覽器一個緩存時間,在緩存時間內,下次請求,直接用緩存,不在時間內,執(zhí)行比較緩存策略。
對于比較緩存,將緩存信息中的Etag和Last-Modified通過請求發(fā)送給服務器,由服務器校驗,返回304狀態(tài)碼時,瀏覽器直接使用緩存。

下面圖幫助理解執(zhí)行過程

緩存的不同來源

from disk cache

從磁盤中獲取緩存資源,等待下次訪問時不需要重新下載資源,而直接從磁盤中獲取。它的直接操作對象為CurlCacheManager。

from memory cache

從內存中獲取資源,等待下次訪問時不需要重新下載資源,而直接從內存中獲取。Webkit早已支持memoryCache。
目前Webkit資源分成兩類,一類是主資源,比如HTML頁面,或者下載項,一類是派生資源,比如HTML頁面中內嵌的圖片或者腳本鏈接,分別對應代碼中兩個類:    MainResourceLoader和SubresourceLoader。雖然Webkit支持memoryCache,但是也只是針對派生資源,它對應的類為CachedResource,用于保存原始數據(比如CSS,JS等),以及解碼過的圖片數據。

區(qū)別

當退出進程時,內存中的數據會被清空,而磁盤的數據不會,所以,當下次再進入該進程時,該進程仍可以從diskCache中獲得數據,而memoryCache則不行。

相似

diskCache與memoryCache相似之處就是也只能存儲一些派生類資源文件。它的存儲形式為一個index.dat文件,記錄存儲數據的url,然后再分別存儲該url的response信息和content內容。Response信息最大作用就是用于判斷服務器上該url的content內容是否被修改。

用戶行為

最后附上一張,用戶行為影響瀏覽器的緩存行為。


實際場景應用緩存策略

頻繁變動的資源

對于頻繁變動的資源,首先需要使用 Cache-Control: no-cache 使瀏覽器每次都請求服務器,然后配合 ETag 或者 Last-Modified 來驗證資源是否有效。這樣的做法雖然不能節(jié)省請求數量,但是能顯著減少響應數據大小。

代碼文件

這里特指除了 HTML 外的代碼文件,因為 HTML 文件一般不緩存或者緩存時間很短。
一般來說,現在都會使用工具來打包代碼,那么我們就可以對文件名進行哈希處理,只有當代碼修改后才會生成新的文件名。基于此,我們就可以給代碼文件設置緩存有效期一年 Cache-Control: max-age=31536000,這樣只有當 HTML 文件中引入的文件名發(fā)生了改變才會去下載最新的代碼文件,否則就一直使用緩存。

參考文章:
https://www.cnblogs.com/chenqf/p/6386163.html
https://www.cnblogs.com/shixiaomiao1122/p/7591556.html
以及yck掘金小冊作者的瀏覽器緩存機制。
特此感謝三篇文章作者。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容