聊一聊http狀態碼,緩存機制,cookie

Http狀態碼

狀態碼 含義
1XX 請求正在被處理
2XX 請求被成功處理
3XX 請求需要附加操作,常見如重定向
4XX 客戶端出錯導致請求無法被處理
5XX 服務端處理出錯

2XX

狀態碼 原因短語 含義
200 OK 請求被正常處理
204 NO CONTENT 請求成功,但是響應的報文中不含實體主體
206 Partial Content 只返回了請求資源的部分

204: 例如option請求,通常被用來做正式請求的預請求,這個請求只需要確認後續的請求能不能通過,即只需要一個結果,而不需要返回其他內容。

206: 在 http的請求中,頭部添加Range用來表示範圍請求,例如

'Range': byte=5001-10000 // 表示本次要請求資源的5001-10000字節的部分

這種情況下,如果服務器接受範圍請求並且成功處理,就會返回 206,並且在響應的頭部返回:

'Content-Range':bytes 5001-10000/10000// 表示整個資源有10000字節,本次返回的範圍爲 5001-10000字節

3XX

狀態碼 原因短語 含義
301 Moved Permanently 資源被永久重定向了
302 Found 資源臨時重定向到location
303 See Other 資源使用GET訪問臨時重定向的location
304 Not Modified 資源未改變,可直接使用緩存
307 Temporary Redirect 嚴格限制重定向不允許從POST轉爲GET
301302的唯一區別就在於一個是臨時,一個是永久。

4XX

狀態碼 原因短語 含義
400 Bad Request 請求中有語法錯誤
401 Unauthorized 未經過認證
403 Forbidden 禁止訪問也就是無權限訪問
404 Not Found 服務端沒有找到所請求的資源

5XX

狀態碼 原因短語 含義
500 Internal Server Error 服務器故障
503 Service Unavailable 服務器暫時無法使用

緩存機制

瀏覽器每次發起請求,都會先在瀏覽器緩存中查找該請求的結果以及緩存標識

瀏覽器每次拿到返回的請求結果都會將該結果和緩存標識存入瀏覽器緩存中

強緩存

強緩存不會向服務器發送請求,直接從緩存中讀取資源,在 chrome 控制檯的 Network 選項中可以看到該請求返回200的狀態碼,並且Size顯示from disk cachefrom memory cache。強緩存可以通過設置兩種 HTTPHeader 實現:ExpiresCache-Control

1. Expires

緩存過期時間,用來指定資源到期的時間,是服務器端的具體的時間點。也就是說,Expires=max-age + 請求時間,需要和Last-modified結合使用。Expires 是 Web 服務器響應消息頭字段,在響應 http 請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器緩存取數據,而無需再次請求。
Expires 是 HTTP/1 的產物,受限於本地時間,如果修改了本地時間,可能會造成緩存失效。Expires: Wed, 22 Oct 2018 08:41:00GMT 表示資源會在Wed, 22 Oct 2018 08:41:00 GMT後過期,需要再次請求。

2. Cache-Control

在 HTTP/1.1 中,Cache-Control是最重要的規則,主要用於控制網頁緩存。比如當Cache-Control:max-age=300時,則代表在這個請求正確返回時間(瀏覽器也會記錄下來)的 5 分鐘內再次加載資源,就會命中強緩存。
Cache-Control可以在請求頭或者響應頭中設置,並且可以組合使用多種指令:

指令 作用
public 響應可被客戶端和代理服務器緩存
private 響應只可被客戶端緩存
max-age=30 緩存 30 秒後過期,需要重新請求
s-maxage=30 覆蓋max-age,作用一樣,只是在代理服務器中生效
no-store 不緩存任何響應
no-cache 資源被緩存,但是立即失效,下次會發起請求驗證資源是否過期,配合協商緩存使用
max-stale=30 30 秒內,即使緩存過期也使用該緩存
min-fresh=30 希望在 30 秒內獲取最新的響應

詳細說明:

  • public:所有內容都將被緩存(客戶端和代理服務器都可緩存)。具體來說響應可被任何中間節點緩存,如 Browser<--proxy1<--proxy2<--Server,中間的proxy可以緩存資源,比如下次再請求同一資源proxy1直接把自己緩存的東西給Browser而不再向proxy2要。
  • private:所有內容只有客戶端可以緩存,Cache-Control默認取值。具體來說,表示中間節點不允許緩存,對於Browser<--proxy1<--proxy2<--Serverproxy會老老實實把Server返回的數據發送給proxy1,自己不緩存任何數據。當下次Browser再次請求時proxy會做好請求轉發而不是自作主張給自己緩存的數據。
  • no-cache:客戶端緩存內容,是否使用緩存則需要經過協商緩存來驗證決定。表示不使用Cache-Control的緩存控制方式做前置驗證,而是使用Etag或者Last-Modified字段來控制緩存。

    需要注意的是,no-cache這個名字有一點誤導。設置了no-cache之後,並不是說瀏覽器就不再緩存數據,只是瀏覽器在使用緩存數據時,需要先確認一下數據是否還跟服務器保持一致。
  • no-store:所有內容都不會被緩存,即不使用強制緩存,也不使用協商緩存
  • max-agemax-age=xxx(xxx is numeric)表示緩存內容將在 xxx 秒後失效
  • s-maxage(單位爲 s):同max-age作用一樣,只在代理服務器中生效(比如 CDN 緩存)。比如當s-maxage=60時,在這 60 秒中,即使更新了 CDN 的內容,瀏覽器也不會進行請求。max-age用於普通緩存,而s-maxage用於代理緩存。s-maxage的優先級高於max-age。如果存在 s-maxage,則會覆蓋掉max-ageExpiresheader。
  • max-stale:能容忍的最大過期時間。max-stale指令標示了客戶端願意接收一個已經過期了的響應。如果指定了max-stale的值,則最大容忍時間爲對應的秒數。如果沒有指定,那麼說明瀏覽器願意接收任何 age 的響應(age 表示響應由源站生成或確認的時間與當前時間的差值)。
  • min-fresh:能夠容忍的最小新鮮度。min-fresh標示了客戶端不願意接受新鮮度不多於當前的 age 加上 min-fresh 設定的時間之和的響應。

3. Expires 和 Cache-Control 兩者對比

其實這兩者差別不大,區別就在於 Expires 是 http1.0 的產物,Cache-Control 是 http1.1 的產物,兩者同時存在的話,Cache-Control 優先級高於 Expires;在某些不支持 HTTP1.1 的環境下,Expires 就會發揮用處。所以 Expires 其實是過時的產物,現階段它的存在只是一種兼容性的寫法。

強緩存判斷是否緩存的依據來自於是否超出某個時間或者某個時間段,而不關心服務器端文件是否已經更新,這可能會導致加載文件不是服務器端最新的內容,那我們如何獲知服務器端內容是否已經發生了更新呢?此時我們需要用到協商緩存策略。

協商緩存

協商緩存就是強制緩存失效後,瀏覽器攜帶緩存標識向服務器發起請求,由服務器根據緩存標識決定是否使用緩存的過程,主要有以下兩種情況:

  • 協商緩存生效,返回304Not Modified
  • 協商緩存失效,返回200和請求結果

協商緩存可以通過設置兩種 HTTP 頭ModifiedETag

1. Last-Modified 和 If-Modified-Since

瀏覽器在第一次訪問資源時,服務器返回資源的同時,在 response header 中添加Last-Modified的 header,值是這個資源在服務器上的最後修改時間,瀏覽器接收後緩存文件和 header;

Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT

瀏覽器下一次請求這個資源,瀏覽器檢測到有Last-Modified這個 header,於是添加If-Modified-Since這個 header,值就是Last-Modified中的值;服務器再次收到這個資源請求,會根據If-Modified-Since中的值與服務器中這個資源的最後修改時間對比,如果沒有變化,返回 304 和空的響應體,直接從緩存讀取,如果If-Modified-Since的時間小於服務器中這個資源的最後修改時間,說明文件有更新,於是返回新的資源文件和200.

但是 Last-Modified 存在一些弊端:

如果本地打開緩存文件,即使沒有對文件進行修改,但還是會造成 Last-Modified 被修改,服務端不能命中緩存導致發送相同的資源.因爲 Last-Modified 只能以秒計時,如果在不可感知的時間內修改完成文件,那麼服務端會認爲資源還是命中了,不會返回正確的資源.既然根據文件修改時間來決定是否緩存尚有不足,能否可以直接根據文件內容是否修改來決定緩存策略?所以在 HTTP/1.1 出現了ETagIf-None-Match;

2. ETag 和 If-None-Match

Etag是服務器響應請求時,返回當前資源文件的一個唯一標識(由服務器生成),只要資源有變化,Etag 就會重新生成。瀏覽器在下一次加載資源向服務器發送請求時,會將上一次返回的 Etag 值放到 request header 裏的If-None-Match裏,服務器只需要比較客戶端傳來的 If-None-Match 跟自己服務器上該資源的 ETag 是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。如果服務器發現 ETag 匹配不上,那麼直接以常規GET 200回包形式將新的資源(當然也包括了新的 ETag)發給客戶端;如果 ETag 是一致的,則直接返回304知會客戶端直接使用本地緩存即可。

3. 兩者之間對比

  • 首先在精確度上,Etag 要優於 Last-Modified。

    Last-Modified 的時間單位是秒,如果某個文件在 1 秒內改變了多次,那麼他們的 Last-Modified 其實並沒有體現出來修改,但是 Etag 每次都會改變確保了精度;如果是負載均衡的服務器,各個服務器生成的 Last-Modified 也有可能不一致。

  • 第二在性能上,Etag 要遜於 Last-Modified,畢竟 Last-Modified 只需要記錄時間,而 Etag 需要服務器通過算法來計算出一個 hash 值。
  • 第三在優先級上,服務器校驗優先考慮 Etag。

強制緩存優先於協商緩存進行,若強制緩存(Expires 和 Cache-Control)生效則直接使用緩存,若不生效則進行協商緩存(Last-Modified / If-Modified-Since 和 Etag / If-None-Match),協商緩存由服務器決定是否使用緩存,若協商緩存失效,那麼代表該請求的緩存失效,返回 200,重新返回資源和緩存標識,再存入瀏覽器緩存中;生效則返回 304,繼續使用緩存。

實際場景應用緩存策略

1. 頻繁變動的資源

Cache-Control: no-cache

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

2. 不常變化的資源

Cache-Control: max-age=31536000

通常在處理這類資源時,給它們的Cache-Control配置一個很大的max-age=31536000(一年),這樣瀏覽器之後請求相同的 URL 會命中強制緩存。而爲了解決更新的問題,就需要在文件名(或者路徑)中添加 hash,版本號等動態字符,之後更改動態字符,從而達到更改引用 URL 的目的,讓之前的強制緩存失效(其實並未立即失效,只是不再使用了而已)。

內容參考:瀏覽器的緩存機制

Cookie的實現機制

Cookie是由客戶端保存的小型文本文件,其內容爲一系列的鍵值對。 Cookie是由HTTP服務器設置的,保存在瀏覽器中, 在用戶訪問其他頁面時,會在HTTP請求中附上該服務器之前設置的Cookie。

  1. 瀏覽器向某個URL發起HTTP請求(可以是任何請求,比如GET一個頁面、POST一個登錄表單等)
  2. 對應的服務器收到該HTTP請求,並計算應當返回給瀏覽器的HTTP響應。
  3. 在響應頭加入Set-Cookie字段,它的值是要設置的Cookie。
  4. 瀏覽器收到來自服務器的HTTP響應。
  5. 瀏覽器在響應頭中發現Set-Cookie字段,就會將該字段的值保存在內存或者硬盤中。
Set-Cookie字段的值可以是很多項Cookie,每一項都可以指定過期時間Expires。 默認的過期時間是用戶關閉瀏覽器時。
  1. 瀏覽器下次給該服務器發送HTTP請求時, 會將服務器設置的Cookie附加在HTTP請求的頭字段Cookie中。
瀏覽器可以存儲多個域名下的Cookie,但只發送當前請求的域名曾經指定的Cookie, 這個域名也可以在Set-Cookie字段中指定)。
  1. 服務器收到這個HTTP請求,發現請求頭中有Cookie字段, 便知道之前就和這個用戶打過交道了。
  2. 過期的Cookie會被瀏覽器刪除。

總之,服務器通過Set-Cookie響應頭字段來指示瀏覽器保存Cookie, 瀏覽器通過Cookie請求頭字段來告訴服務器之前的狀態。 Cookie中包含若干個鍵值對,每個鍵值對可以設置過期時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章