HTTP系列之:HTTP缓存
文章目錄
- 簡介
- HTTP中的緩存種類
- HTTP中緩存響應的狀態
- HTTP中的緩存控制
- 緩存刷新
- revving
- 緩存校驗
- Vary響應
- 總結
簡介
為了提高網站的訪問速度和效率,我們需要設計各種各樣的緩存,通過緩存可以避免不必要的額外數據傳輸和請求,從而提升網站的請求速度。對于HTTP協議來說,本身就自帶有HTTP緩存。
今天我們就深入探討一下HTTP中的緩存機制和使用。
HTTP中的緩存種類
緩存就是將請求的資源在本地保存一份拷貝,從而在下一次請求的時候,直接返回該拷貝,不用再從服務器下載資源,從而減少了資源的傳輸提升了效率。
除了直接訪問和返回資源之外,HTTP中的緩存可以分成兩類,一種是共享cache,也就是說不同的客戶端都可以從該共享cache中獲取資源,并且這些資源是多個客戶端可以訪問的。還有一種是私有cache,這意味著該cache只能用戶或者客戶端私有訪問,其他用戶是無權訪問的。
私有cache很好理解,我們常用的瀏覽器中的cache基本上就是私有cache,這些cache是瀏覽器獨有的,并不會共享給其他的瀏覽器。
共享cache主要用在一些web代理上,比如web代理服務器,因為web代理服務器可能會為眾多的用戶提供資源服務,對于這些用戶共同訪問的資源就不必要每個用戶保存一份了,只需要在web代理服務器中保存一份即可,這樣可以減少資源的無效拷貝。
HTTP中緩存響應的狀態
對于HTTP緩存來說,一般緩存的是GET請求,因為GET請求除了URI之外,并沒有其他多余的參數,并且其表示的意義是從服務器獲取資源。
不同的GET請求,會返回不同的狀態碼。
如果是成功返回資源,則會返回200表示OK。
如果是重定向,則返回301。如果是異常,則返回404。如果是不完全的結果,則會返回206。
HTTP中的緩存控制
HTTP中的緩存控制是通過HTTP頭來表示的。在HTTP1.1中加入了Cache-Control,我們可以通過Cache-Control來控制請求和響應的緩存情況。
如果不需要緩存,則使用:
Cache-Control: no-store如果需要對客戶端的緩存進行驗證,則使用:
Cache-Control: no-cache如果要強制進行驗證,則可以使用:
Cache-Control: must-revalidate在這種情況下,過期的資源將不會被允許使用。
對于服務器來說,可以通過Cache-Control來控制緩存是private或者public的:
Cache-Control: private Cache-Control: public還有一個非常重要的緩存控制就是過期時間:
Cache-Control: max-age=31536000通過設置max-age,可以覆蓋Expires頭,表示在這個時間區間范圍之類,該資源可以看做是最新的,不需要重新從服務器獲取。
Cache-Control是HTTP1.1中定義的header字段,在HTTP1.0中也有一個類似的字段叫做Pragma。通過設置 Pragma: no-cache可以得到類似Cache-Control: no-cache的效果。也就是強制客戶端重新提交緩存到服務器端進行校驗。
但是對于服務器端的響應來說,并不包含Pragma,所以Pragma并不能完全替代Cache-Control。
緩存刷新
緩存存放在客戶端之后,就可以在請求的時候被使用了。但是為了安全起見,我們需要給緩存設置一個過期時間,只有在過期時間之前的時間范圍,緩存才是有效的,如果超過了過期時間,則需要從服務器重新獲取。
這樣的機制能夠保證客戶端獲取到的資源始終是最新的。并且能夠保證服務器端對資源的更新能夠及時到達客戶端。
如果客戶端的資源在過期時間之類,那么這個資源的狀態就是fresh,否則資源的狀態就是stale。
如果資源是stale狀態的,該資源并不會立即從客戶端清理出去,而是在下一次的請求中,向服務器發送一個If-None-Match的請求,判斷該資源在服務器端是否仍然是fresh狀態的,如果該資源并沒有發生變化,則返回304 (Not Modified),表示該資源仍然有效。
而這個fresh的持續時間就是通過"Cache-Control: max-age=N" 來判斷的。
如果響應中并沒有這個頭,則會去判斷 Expires header 是否存在,如果存在那么fresh的時間就可以使用Expires - Date 來進行計算。
如果響應中連Expires header都沒有,那么怎么去判斷資源的fresh時間呢?
這種情況下會去查找Last-Modified header,如果這個header存在的話,那么fresh時間就是(Date - Last-modified )/ 10 。
revving
為了提升HTTP請求的效率,我們當然希望緩存時間越長越好,但是前面我們也提到了,緩存時間過長會導致服務器資源更新困難的問題。怎么解決呢?
對于那些不經常更新的文件,請求他們的URL可以由文件名+版本號來決定。同一個版本號表示該資源內容是固定不變的,我們可以對其緩存一個非常長的時間。
當服務器資源內容發生變化之后,只需要在請求的時候更新版本號即可。
雖然這樣的操作會造成服務器資源的修改同時要修改客戶端請求的版本,但是在現代前端打包工具的幫助下,這并不是一個很大的問題。
緩存校驗
當緩存的資源過期之后,有兩種處理方式,一種是重新從服務器請求資源,一種是對緩存資源進行再次校驗。
當然再次校驗需要服務器的支持,并需要設置"Cache-Control: must-revalidate"請求頭。
那么客戶端怎么去校驗資源是否有效呢?很明顯我們不能把資源從客戶端發送到服務器端進行校驗,這樣的操作方式太過復雜,并且在文件比較大的請求下,會造成資源的浪費。
我們很容易想到的一種方法是對資源文件進行hash運行,只要發送這個hash運算的結果進行對比即可。
當然,在HTTP中,提供了一個ETags header,這header可以看做是資源的唯一標記,用來在客戶端和服務器端進行校驗。這樣客戶端就可以請求一個If-None-Match,讓服務器判斷該資源是否match。這種判斷被稱為強校驗。
還有一種弱校驗的方式,如果響應中帶有Last-Modified,則客戶端可以請求一個If-Modified-Since,來向服務器詢問該文件是否發生了變化。
對于服務器端來說,它可以選擇是否進行文件的校驗,如果不進行校驗,則可以直接返回一個200 OK狀態碼,并直接返回資源。如果進行校驗,則返回一個304 Not Modified,表示客戶端可以繼續使用緩存的資源,同時還可返回一些其他的header字段,比如更新緩存的過期時間等。
Vary響應
在服務器響應的時候,可以帶上Vary header。這個Vary header的值是響應頭中的某個key,比如Content-Encoding,表示對某個encoding的資源進行緩存。
比如客戶端首先請求:
GET /resource HTTP/1.1 Accept-Encoding: *服務器端返回:
HTTP/1.1 200 OK Content-Encoding: gzip Vary: Content-Encoding則將會把資源和gzip類型的Content-Encoding一起緩存起來。
當客戶再次請求:
GET /resource HTTP/1.1 Accept-Encoding: br因為當前緩存的資源encoding方式是gzip,和客戶端接受的encoding方式并不一樣,所以重新需要從服務器獲取:
HTTP/1.1 200 OK Content-Encoding: br Vary: Content-Encoding這時候,客戶端又緩存了一個br格式的資源。
下次客戶端再次請求br類型的資源,就可以命中緩存了。
總結一下,Vary的意思是將資源再通過其他的類型比如encoding進行區分和緩存。
但是這樣也會造成資源重復存儲的問題,同一個資源因為編碼格式的不同被緩存了很多份。為了解決這個問題,就需要對資源請求進行標準化。
所謂標準化,就是在請求之前對請求的encoding方式進行校驗,只選擇其中的一種編碼方式進行請求,從而避免資源多次緩存的情況。
總結
到此,HTTP緩存就介紹完畢了,大家可以在實際的應用中對HTTP緩存加深理解。
本文已收錄于 http://www.flydean.com/04-http-cache/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的HTTP系列之:HTTP缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: netty系列之:轻轻松松搭个支持中文的
- 下一篇: HTTP系列之:HTTP中的cookie