1. 程式人生 > >快取知識體系之-瀏覽器快取

快取知識體系之-瀏覽器快取

【上篇】

前言

現在我們把注意力關注到瀏覽器本身,那麼我們要知道的是瀏覽器本質上是一個HTTP代理,它幫助使用者傳送HTTP請求給,然後Web伺服器響應請求,返回資料給瀏覽器,瀏覽器進行本地渲染展示給使用者。那麼瀏覽器能不能把Web伺服器返回的網頁或者圖片、css、js等這些資源儲存下來,下次我們再訪問相同的資源的時候直接讀取快取呢?

瀏覽器快取協商

現在我們有兩個夥伴,瀏覽器和Web伺服器,Web伺服器希望能快取一些資料到瀏覽器上,但是瀏覽器並不知道哪些資料要快取,哪些資料不快取,那麼他們兩個就需要對話。由於我們瀏覽器和Web伺服器本身的通訊協議就是HTTP,那麼是不是就可以通過HTTP來進行對話呢,我們把這個瀏覽器和Web伺服器之間有關快取的對話稱之為“快取協商”,雖然有點像小學課文的名詞解釋,但是我們現在知道了一個專業的名詞-“快取協商”。

Last-Modified

瀏覽器和Web伺服器快取協商的第一個方法是Last-Modified,也就是最後修改時間。我們知道我們的網頁存放在Linux伺服器上會有三個時間,我們可以使用stat來獲取。

快取知識

那麼Web伺服器預設情況下可以通過stat()系統呼叫獲取到靜態檔案index.html在硬碟上的最後修改時間,而且在響應HTTP請求的時候回會為靜態檔案在HTTP響應頭部自動生成最後修改時間。比如下面我們使用Firefox的Firebug外掛來檢視alidns.com(阿里提供的公共DNS)。

快取知識

在上面的這個圖中,我們開啟http://alidns.com,瀏覽器傳送的第一個請求GET alidns.com,我們可以看到Web伺服器返回200的狀態碼,同時在響應頭中,告訴了瀏覽器,它這個檔案的最後修改時間。好的,我們先不要著急,先來研究下這些快取存放在哪裡。對於firefox瀏覽器來說,是使用二進位制格式儲存的,不過我們可以使用about:cache來檢視。直接在瀏覽器的位址列輸入即可。

瀏覽器快取

我們可以看到firefox預設有兩種快取儲存的地方,一個是記憶體中,一個是儲存在磁碟中,還有一個是應用本身的快取。(如果你的瀏覽器已經開啟很久,可能需要清空快取後,才能更快的找到剛才的alidns.com的快取)我們現在再次重新整理下頁面,你可以按F5、回車鍵或者firefox上面的過載頁面的圖示。

瀏覽器快取

我們再次使用firefox的firebug看看發生了什麼,首先之前的返回的狀態碼由200變成了304。我們先看下面的請求頭的資訊。再第二次訪問alidns.com的時候瀏覽器在請求頭部增加了If-Modified-Since      Fri, 11Jul 2014 03:25:04 GMT的內容,意思是詢問瀏覽器:瀏覽器大哥,請問下這個檔案在我這個時間後有沒有更改過,如果沒有更改過,我就使用我本地快取給使用者呈現頁面了哦。再看最上面,Web伺服器非常果斷的回覆304,也就是這個檔案在你之前儲存的最後修改時間後,沒有更改過,你可以使用本地快取。

好的,我們可以看到瀏覽器和Web伺服器協商的很愉快,對於訪問我們站點的使用者來說,如果之前訪問過我們的網站,那麼第二次開啟相同的靜態頁面,就不會產生實際的檔案傳輸。為我們節約了伺服器的頻寬,同時由於瀏覽器直接使用本地快取呈現給使用者,所以使用者開啟我們站點的速度也是非常的快。

 Etag

我們現在要介紹另外一個快取協商的方法,因為在某些場景下Last-Modified可能工作的並不愉快,比如有一種情況,我們的Web伺服器上面的檔案最後修改時間會頻繁的變動,但是檔案內容卻沒有修改。那麼對於Last-Modified的快取協商,每次都會重新獲取檔案,而不會使用快取。

還有一種生產中比較常見的場景,比如我們在叢集環境中,相同的網頁可能被儲存在不同的伺服器上,在負載均衡的時候使用者的請求會被分配到這些不同的伺服器中,但是我們很難保證相同的檔案在所有伺服器上的最後修改時間都是一致的。那麼這樣使用者請求被分發到時間不對的伺服器上時,可能會導致瀏覽器會重新獲取網頁內容。

Etag這個時候站出來勇敢的承擔一切,HTTP1.1協議中並沒有規定Etag的具體格式和生產的方法,總之Web伺服器可以給每一個靜態檔案都生產一個標籤,或者叫指紋,總之是一個唯一識別符號(比如給檔案做一個md5),當檔案內容改變的時候就修改這個標籤,這樣瀏覽器和Web伺服器之間就通過詢問網頁的Etag是否改變來進行快取協商。

這裡我不準備再次截圖,讀者可以自己使用firebug觀察下etag的工作方式,我要強調一下就是同時存在Etag和Last-Modified的時候,Etag的優先順序要高。

Expires

我們先回過頭在看之前的兩種快取協商辦法,瀏覽器給Web伺服器傳送HTTP請求來詢問是否可以使用快取。如果Web伺服器告訴瀏覽器使用快取,那麼瀏覽器就直接使用快取呈現給使用者,你是否發現,這個詢問的HTTP請求是不是也佔用了Web伺服器的資源,即便不產生任何的資料傳輸,使用者還是需要等待這個請求傳送到響應完畢。那麼能不能不傳送HTTP請求呢?比如第一次瀏覽器請求完畢後,Web伺服器告訴瀏覽器這個網頁儲存1小時,你1個小時之內不要再過來煩我哦。

和Last-Modified、Etab一樣我們常用的Web伺服器Apache、Nginx都支援過期時間(Expires)的設定,當瀏覽器第一次請求資源的時候,Web伺服器設定一個過期時間,那麼在這段時間內,瀏覽器會直接使用本地快取,而不傳送任何的快取協商。請看下面這個Nginx的配置例項:

瀏覽器快取

這裡將所有後綴為gif、jpg、jpeg、png、bmp、swf這些資源設定了30天的過期時間,而將js和css設定了1個小時的過期時間。你可以繼續使用firebug來觀察使用expires的情況。

【下篇】

開始之前

昨天的公眾號講解的瀏覽器快取的上篇,那麼開始之前你可以需要了解的是在Web架構中還都有哪些快取。剛好我準備了一個相對完整的《快取知識體系》:https://www.unixhot.com/page/cache

前言

三種瀏覽器協商的辦法讓瀏覽器和我們Web伺服器愉快的玩耍,我們把使用者的瀏覽器當做我們派發到千家萬戶的快取管理員,他們幫我們Web站點存放和管理快取,並按照協商的辦法給使用者呈現,突然有一天有一個看似巧合而又普遍的時間打破了這一切。

如果伺服器和使用者瀏覽器的時間不一致呢?我們Web伺服器在工程師的管理下可以自動更新時間,保證時鐘同步,但是我們怎麼保證使用者本地電腦的時間都是對的呢?我們並沒有辦法!那這樣就會帶來一個問題。比如上面我們將css和js的過期時間為一個小時,但是使用者的電腦比伺服器時間晚了2個小時,那麼使用者訪問Web站點每次瀏覽器都會認為立即過期,重新獲取資料而不會使用本地快取,所以我們需要有機制來解決這個問題。

Cache-Control

HTTP1.1中的Cache-Control出現了,用於彌補Expires的不足,通過max-age告訴瀏覽器快取過期的相對時間,注意這個相對時間是相對於本地瀏覽器時間而設定的。

瀏覽器快取

可以看到上圖中。max-age的為3600秒,也就是我們在Nginx設定的一個小時。這樣瀏覽器就會根據使用者本地時間來計算一個小時後,再讓緩過期。

還有一個小細節,不知道你之前是否注意到。瀏覽器和Web伺服器之間協商的時間都是GMT時間,而我們中國實用的時間是GMT+8的時區,所以我們看到的時候比實際上少了8個小時,但是這絲毫不影響他們之間的協商。

Cache-control除了max-age還有以下一個不同的引數:

  • Cache-control: public 表示快取的版本可以被代理伺服器或者其他中間伺服器識別。
  • Cache-control: private 意味著這個檔案對不同的使用者是不同的。只有使用者自己的瀏覽器能夠進行快取,公共的代理伺服器不允許快取。
  • Cache-control: no-cache 意味著檔案的內容不應當被快取。這在搜尋或者翻頁結果中非常有用,因為同樣的URL,對應的內容會發生變化。

瀏覽器快取重新整理

您真的會使用瀏覽器的重新整理按鈕嗎?不同的重新整理機制可以導致不同級別的快取失效,讓我們來一起測試下。

1. 在位址列中輸入網址後按回車或點選轉到按鈕。

瀏覽器會對所有沒有過期的內容直接使用本地快取,這個時候Last-Modified、Etag、和Expires均不會受重新整理的影響。

2. 按F5或瀏覽器重新整理按鈕。

瀏覽器會在請求中附加必要的快取協商,但不允許瀏覽器直接使用本地快取。這個時候Last-Modified、ETag就要受影響,要發起快取協商的動作,但是對Expires無效。

3. 按Ctrl+F5或按Ctrl並點選重新整理按鈕。

這種方式就是我們說的強制重新整理,每次瀏覽器都發起一個全新的請求,不使用任何快取。

不知道你是否發現問題,如果你修改了一個設定了Expires快取1小時的檔案,難道你要通知所有使用者使用Ctrl+F5強制重新整理頁面嗎?這顯然不太可能,接下來我們要來談談瀏覽器快取過期。

瀏覽器快取過期

我們最終對於我們現在的所有靜態資源使用了expires的過期時間,徹底消滅了HTTP快取協商中的HTTP請求,而且由於Cache-Control中適應本地的過期時間,我們應該明顯的看到網站流量的下降,但是如果有一天,我們上線了一個js,結果由於bug,我們必須馬上替換這個js檔案。你們團隊敏捷的修復這個bug併發布上線。

這個時候問題來了:然而你會發現已經訪問過的使用者,依然使用的本地快取的舊js,造成網站故障。這並不能怪瀏覽器,是你的Web伺服器告訴他3小時之內不要過來煩我,直接使用瀏覽器本地快取:比如你再nginx設定的是

瀏覽器快取

解決的思路就是最簡單的就是修改檔名,這樣瀏覽器對於新的js檔案肯定是會重新獲取的,如果你的Web框架有這樣的支援,這是再好不過的了,那麼還有一種方式就是給檔案使用引數加上時間戳:

http://www.example.com/js/code.js?20160101

這樣瀏覽器再解析這個url的時候,就會認為這個是一個新的檔案,會重新獲取並不會使用瀏覽器本地快取。那麼不同的語言、不同的Web框架都會有不同的實現方式,這裡不在舉例。

小技巧:如何檢視已釋出的小乾貨?

  1. 進入“運維社群”公眾號;
  2. 點選右上角“小人”圖示;
  3. 點選“檢視歷史訊息”。

分享嘉賓

趙舜東

江湖人稱趙班長,曾在武警某部負責指揮自動化的架構和運維工作,2008年退役後一直從事網際網路運維工作。曾帶團隊負責國內某電商的運維工作,SaltStack中國使用者組發起人、《saltstack入門與實踐》作者,《運維知識體系》作者。

趁現在,關注我們

牛人並不可怕,可怕的是牛人比我們還努力!關注“運維社群(微信ID:cloud-oaas)”公眾號,每天利用空閒時間閱讀一篇技術原創乾貨,滿滿的小幸福。

瀏覽器快取

長按圖片,選擇“識別圖中二維碼”,關注我們。

原創宣告:本文章由趙班長原創,請必須全文轉載,包括本行。