天天看點

HTTP系列之:HTTP緩存簡介HTTP中的緩存種類HTTP中緩存響應的狀态HTTP中的緩存控制緩存重新整理緩存校驗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/

最通俗的解讀,最深刻的幹貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

歡迎關注我的公衆号:「程式那些事」,懂技術,更懂你!

繼續閱讀