文章出處

隨著近些年社交網站的流行,越來越多的人學會了“刷”網頁 ── 刷微博,刷朋友圈,刷新聞,刷秒殺頁。這里的“刷”,就是刷新的意思,在瀏覽器里,你可以通過點擊刷新按鈕,或者用快捷鍵,或者移動端的下拉操作來進行刷新。

但普通網民不知道的是,通過刷新操作導致的頁面加載和通過其他操作(比如點擊頁面鏈接,地址欄輸入網址并回車,點擊收藏夾網址等)導致的頁面加載有一點不同,那就是刷新操作會給該頁面的請求本身以及頁面里所引用的資源們(JS,CSS,圖片等)的請求加上 If-Modified-Since 和 If-None-Match 請求頭(如果已經有緩存且有 Last-Modified/ETag 響應頭的話),服務器會根據這兩個請求頭判斷該資源有沒有更新過,如果沒有,就返回不帶響應體的 304 響應,告訴瀏覽器:“用緩存吧”,如果更新過了,則把更新后的資源放在響應體里返回 200 響應。

我們把上面說的這種帶有 If-Modified-Since 或 If-None-Match 請求頭的 HTTP 請求叫做條件請求,除了刷新操作,條件請求還會發生在緩存過期的時候,也就是已緩存時長大于 Cache-Control 響應頭中的 max-age 字段指定的秒數的時候。

條件請求是設計用來更新資源的,但實際情況是,在現如今的網站開發中,尤其是大型網站,會依賴適當的過期時長或者讓用戶手動刷新來更新頁面嗎?比如把緩存時長設置成一小時,新版頁面上線了,用戶都看不到效果,老板過來問:“這怎么回事啊,不是上線了嗎”,開發回答:“要等一小時緩存過期啊,你也可以刷新一下就看到效果了~”。顯然不可能這樣,對于那些有更新需求的靜態資源,常見的是 JS、CSS,我們都會在它的 URL 里加上點東西,時間戳、版本號、哈希值等,可以放在 URL 的路徑里,也可以放在查詢參數里,因為只要 URL 變了,瀏覽器就認為是不同的資源,就會重新下載;還有一些靜態資源是完全沒有更新需求的,比如你在微博上傳的那些圖片,同一個 URL 對應的資源是永遠不會變的。

上面說的這兩種情況,其實是一種,就是它們永遠沒有更新的需求,它們是不可變的,是 immutable 的,304 用在它們身上完全沒有意義,全是浪費。雖然每個 304 請求的往返體積只有 1k 左右,但架不住多啊。而且就算只有一個字節,也會導致頁面展現變慢,讀本地文件和讀網絡資源還是有本質區別的。

Facebook 在一年前意識到了這個問題,它的工程師給制定 HTTP 標準的 IETF 工作組發了封郵件,里面說到,Facebook 使用版本號來更新靜態資源,還給靜態資源設置了幾乎不可能過期的緩存時長,但發現仍然有 20% 的請求是無意義的條件請求(必然 304),這給服務器性能帶來很大傷害,他們研究發現是因為 Facebook 頁面 pv 有 2% 來自用戶的刷新操作,他們希望 HTTP 協議能給 Cache-Control 響應頭增加一個屬性字段表明該資源永不過期,瀏覽器就沒必要再為這些資源發送條件請求了。

今年四月份,Mozilla 的人覺的 Facebook 提的這個建議很好,于是他們在 Firefox 49 里實現了 Cache-Control: immutable。immutable 的推薦用法是和那些超大的 max-age 配合使用,比如 1年:Cache-Control: max-age=31536000, immutable,甚至 10年, 但通常情況下,1 年就夠了,因為:1. 對于單個緩存來說,它在某個瀏覽器里存活的時長不可能超過一年,瀏覽器的緩存空間都有上限,Firefox 256M,Chrome 320M,舊的緩存會時不時被清掉。2. 一個用戶不大可能一年后還來同一個頁面,且那個頁面還沒改版。對緩存時長來說,1 年就代表永遠了。

但這只是推薦做法,immutable 并不是真的只能應用在那些永不過期的資源上,也可以配合較小的 max-age 來使用,比如一些個人博客,或者一些不太講究及時更新的站點,可以設置成 Cache-Control: max-age=3600, immutable,表明該資源能存活一小時,在一小時之內,即便用戶刷新也不要發送條件請求,在過期之后,瀏覽器會發送不帶一個不帶 If-Modified-Since 和 If-None-Match 的請求來更新資源,這里需要注意,一旦被標志成 immutable,則這個資源不可能返回 304 響應了,只有 200。

目前 Firefox 的實現里,只對 HTTPS 資源開放 immutable 屬性的支持,我通過 Fiddler 在本地篡改了淘寶搜索頁面 https://s.taobao.com/search?q=連衣裙 里所有資源的 Cache-Control 響應頭,在原值尾部加上 “, immutable”。篡改之前,假如我刷新一下此頁面,會導致數十個 304 響應:

篡改之后的刷新效果:

注意那些帶有 cached 字樣的 200 請求,那些請求實際上根本不是真正的請求,只是一次本地讀取文件的操作。

目前 Facebook 還沒有反饋 immutable 的測試數據,畢竟 Firefox 49 還不是正式版,以后應該會有的。不過考慮到現在 Firefox 的市場占有率,也許 Chrome 實現之后才會得到更多人的關注, Chrome 也表示了有意愿去實現。不過我在 GitHub 搜了一下,倒是發現 W3C 的網站Firefox 附加組件網站準備實現。

immutable 只有在你的網站被頻繁刷新的情況下才有較大的意義。還有雖然它是向后兼容的,但可能一些 CDN 服務器在識別 Cache-Control 時因不認識這個屬性,導致最終返回給瀏覽器的響應丟失了 immutable,推特上有反應 Akamai 就這么干了。

少數人知道的強制刷新功能(Ctrl+F5/Shift+Command+R)以及開發者工具的跳過緩存功能優先級應比 immutable 更高。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()