摘要:緩存緩存主要是通過請求和響應報文頭中的對應信息,來控制緩存的策略。就會返回一個的狀態碼,表示可以繼續使用客戶端本地緩存的數據,并刷新超時時間。與之相對的,則表示當前響應是針對單個用戶的,并非通用數據,因此不建議任何中間緩存對其進行緩存。
題圖:by @joewakeford
一、序Hi,大家好,我是承香墨影!
HTTP 協議在網絡知識中占據了重要的地位,HTTP 協議最基礎的就是請求和響應的報文頭(Header),大多數 Http 協議的使用方式,都是依賴設置不同的 HTTP 請求/響應 的 Header 來實現的。
本系列《實用 HTTP》就拋開常規的 Header 講解式的表述方式,從實際問題出發,來分析這些 Http 協議的使用方式,到底是為了解決什么問題?同時講解它是如何設計的和它實現原理。
HTTP 協議是一種無狀態的“松散協議”,它不會記錄不同請求的狀態,并且因為它本身包含了兩端(客戶端和服務端),根據請求和響應來區分,它大部分的內容都只是一個建議,其實雙邊是可以不遵守此建議的。例如:服務端說,這個數據緩存有一天的時效性,但是客戶端可以說,我不聽我不聽,我就要每次去重新請求。
“這里寫了建議零售價 2 元...”“哦,不接受建議!”
說到緩存,本文就來說說 HTTP 緩存相關的內容。
二、HTTP緩存使用 2.1 為什么需要緩存緩存說白了就是為了快,無論是從磁盤到內存還是從網絡到本地,都是為了在下次實用此資源的時候,能夠快速響應,避免多次的 I/O 操作。
通過網絡獲取資源,是一件耗時的操作,較大的資源還會需要客戶端和服務端之間進行多次往返通信,這不但會增加客戶端響應的時間,同時還會增加網絡流量。
在 HTTP 協議中,天然就有對緩存的支持,瀏覽器和 App 使用的開源網絡庫中,都是利用 HTTP 緩存來實現對資源的緩存。
瀏覽器是天然支持 HTTP 緩存,開源庫則需要進行一些例如存規則和緩存的資源存放路徑之類的簡單設定。2.2 設計一個緩存策略
那如果讓我們來設計緩存的策略,首先有兩個重要的指標需要考慮。
1. 緩存失效
既然緩存主要是針對數據的復用,那我們就需要有一個條件來判定當前緩存的數據,是否依然有效。
總是不能一次緩存,終身使用吧,我們還需要在緩存失效之后,重新獲取新的數據并進行緩存。這個前提就是,緩存都需要有一個失效的策略。
2. 減少讀取
雖然緩存會有失效策略,但是這只是客戶端單方面認為失效,此時應該再去服務端重新獲取一遍數據。
可有些情況下,其實資源可能依然有效,并沒有發生變動。那就需要有一個策略,讓服務端通知客戶端,當前緩存依然有效,可以繼續使用。這樣在減少傳輸流量之外,也可以加快相應時間,提高效率。
這就是一個好的緩存策略必須要考慮的地方,實際上 HTTP 緩存,也是這樣設計的。
2.3 HTTP 緩存HTTP 緩存主要是通過請求和響應報文頭中的對應 Header 信息,來控制緩存的策略。
這里主要涉及兩個 Header:
Cache-Control:設定緩存策略,是否使用緩存,超時時間是多少。
ETag:當前返回數據的驗證令牌,可能是 Hash 值也可能是其他指紋,主要用于在下次請求的時候攜帶上,讓服務端依此判斷當前數據是否有更改。
服務端在返回響應數據的時候,會在報文頭中,增加用于描述當前響應的內容類型、數據長度、緩存策略(Cache-Control)、驗證令牌(ETag)等信息。
例如上圖就表示了一次請求響應的事務,大概客戶端請求一個文件的時候,服務端返回了一個 200 的狀態碼,表示響應正常,響應的數據長度為 1024 個字節,建議客戶端將此資源緩存最多 120 秒,并且提供了一個指紋令牌(“cxmyDev123”),用來作為當前數據的唯一標識。
2.4 ETag 數據令牌Cache-Control 中設定的 max-age 很好理解,就是設定緩存超時的時間,HTTP 緩存是限定一個超時的秒數,來確定緩存失效的時間。
上古時期還會使用 expires 來決定超時的日期,但是已經被廢棄了,如果和 Cache-Control 同時存在,以 Cache-Control 為準。
在此時間間隔范圍內,客戶端不會再向服務端發送新的請求。當資源距離上一次緩存的時間間隔,大于 120 秒后,客戶端才會再次向服務端發送請求。
假如沒有數據令牌的情況下,大概步驟應該是這樣的:
1. 客戶端會首先找到本地緩存,然后發現它已經失效,無法再次使用。
2. 客戶端再次向服務端發出新的請求,并獲取完整的數據再次進行緩存。之后再刷新該緩存的超時時間。
但是這是一件效率非常低的事情,服務端并無法確定所持有的源資源什么時候會失效,所以提供的 max-age 值,只是一個參考值,是需要取平衡的,太短會導致請求頻繁,太長又會導致無法及時刷新客戶端資源。而此時再次請求的時候,是存在一定的概率,客戶端緩存的數據和服務端上持有的數據是一致的,我們就不需要再次對此數據資源進行二次緩存,直接使用客戶端之前緩存的數據即可,同時還需要刷新緩存超時時間。
這正是數據驗證令牌(ETag)想要解決的問題,服務端生成并返回的這個數據指紋令牌,通常就是返回數據的 Hash 值或者其他數據指紋,客戶端無需關心它的生成規則,只需要知道它是當前數據的一個唯一標識。
客戶端需要在下次請求時將其通過 If-None-Match 這個請求報文頭,將此驗證令牌發送至服務端,如果數據令牌指紋和服務端當前的數據一致,則標識資源未發生新的變化。就會返回一個 304 的狀態碼,表示可以繼續使用客戶端本地緩存的數據,并刷新超時時間。注意當響應碼為 304 的時候,它是不包含數據內容的。
通常此緩存操作對我們都是透明的,它是瀏覽器和開源網絡庫的基本實現,我們無需自己去判斷 max-age 和 ETag 的值,這一步我們只需要確定服務端對此有支持即可。
這里只是提到了 If-None-Match,它標識比較 ETag 是否不一致,除此之外,還有一些其他的相關報文頭,例如 If-Match,有興趣可以查閱相關資料。2.5 Cache-Control
前面舉的例子中,我們只為 Cache-Control 設定了一個 max-age,但是其實還有一些更豐富的配置。
從緩存性能最優化的角度來看,最佳的緩存是無需與服務端通信的緩存,可以通過緩存來消滅網絡延遲以及數據請求,從而來提高用戶的體驗。
Cache-Control 是在 HTTP/1.1 中被定義的,它可以用于取代之前的緩存策略,現在所有的瀏覽器都支持 Cache-Control ,它已經成為一種通用的標準。
Cache-Control 還有一些更靈活的配置,用來對緩存做一些更細致的操作。
1. “no-cache” 和 “no-store”
這兩個參數都表示每一次請求,都需要真實的發送一個網絡請求。
它們之間的區別在于,“no-cache”并不是真的不緩存數據,它只是要求每次都確認資源是否過期,也就是它會利用數據令牌 ETag 來一定程度的減小傳輸的流量。
而 “no-store” 完全是要求客戶端,每次都重新請求數據并下載最新的數據,不做任何緩存處理。這種不緩存的策略,也包括中間連接的代理、網關 等中間傳輸的通道,也一并不對數據進行緩存,每次都從源服務器上獲取數據。
2. “public” 和 “private”
“public” 是一種默認的策略,表示當前緩存是開放的,任何請求響應的中間環節,都可以對其進行緩存,如果我們不顯式指定,則當前為 “public” 緩存。
與之相對的 “private”,則表示當前響應是針對單個用戶的,并非通用數據,因此不建議任何中間緩存對其進行緩存。例如:瀏覽器就是一個比較私人的緩存源,它會緩存 “private” 的緩存,而 CDN 則不會。
三、最佳的緩存策略樹前面提到,緩存的核心目的就是為了快,能讓下次使用的時候快速復用。所以在理想情況下,我們應該將響應數據盡可能多的緩存,盡可能的緩存足夠長的時間,并且為每個資源提供多帶帶的數據驗證令牌,以便在時間過期之后快速校驗。
但是任何事情都是要取其平衡點的,不存在什么最佳緩存策略,并非所有響應資源都需要加緩存,這就需要根據業務場景來設定。
這里給出一個增加 HTTP 緩存的通用策略樹,你在對響應增加緩存的時候,可以參考它來執行。
正常情況下,我們針對不同的響應屬性,會對它設置不同的緩存策略,下面根據場景,舉幾個例子。
3.1 用戶相關的數據和單個用戶緊密相關的數據,通常我們是不建議使用緩存的,但是依然存在幾個等級。
1. 嚴格不使用緩存
Cache-Control:no-store
2. 允許客戶終端緩存,但是每次使用都需要確認
Cache-Control:no-cache ETag:"cxmyDev1234"
3. 允許客戶終端短時間緩存
Cache-Control:private max-age=600 ETag:"cxmyDev1234"3.2 通用數據
一些通用響應資源,更新的頻率非常的低,我們可以根據需要調整 max-age 的大小即可。
Cache-Control:max-age=86400 ETag:"cxmyDev1234"四、廢棄和更新緩存的響應
緩存的策略,一旦確定并下發到客戶端,服務端就失去了對齊的控制權。也就是說,如果我們設定了 max-age,在此資源有效期超時之前,哪怕服務端的源資源已經被替換修改,我們也沒有一個合適的時機去通知客戶端更新新的響應數據。
那么有沒有什么好的策略去標記資源廢棄?同時又能友好的利用緩存策略。
在互聯網上,所有服務上的資源,都有一個對應的 URL(統一資源定位符),它可以明確說明如何從一個精確且固定的位置獲取資源。而 HTTP 緩存,也是依賴于 URL 的,注意 URL 是大小寫敏感的,同一個 URL 表示同一個請求響應,依此來判斷緩存和后續緩存的復用。
所以我們是可以在 URL 上做文章的。
4.1 瀏覽器的廢棄策略前面提到,瀏覽器是天然支持 HTTP 緩存的,對于瀏覽器來說,它所面對的就是一個個 HTML 頁面,頁面內會包含一些 CSS、Image、JavaScript、JSON 資源和數據。
針對不同的資源和數據,我們可以在其 URL 上,增加數據令牌指紋,當資源變動的時候,同時也去刷新改指紋令牌。
到這里就很好理解了:
HTML 頁面,使用 no-cache,強制每次都向源服務器確認數據。
CSS文件通常變動的頻率非常低,所以可以允許中間層緩存,并且緩存時間為一年不過期。
JavaScript內有業務邏輯,可以設定為只允許客戶終端緩存。
getUserInfo,是為個人用戶數據相關,這里推薦可緩存,但是需要每次向服務器重新確認。
4.2 App 接口的緩存策略在 App 中使用的接口,其實和網頁又不一樣,HTML 網頁的結構類似一個樹形結構,先通過獲取 .html 文件獲取其內所有資源的表,然后依次根據緩存策略進行訪問。
但是在 App 中,和服務器的交互都是通過數據接口來實現的,就不存在最開始獲取一個類似 HTML 文件這樣的樹形接口,每個接口都是一個個“孤島”,可以多帶帶存在。我們就無法提前知道某個接口的響應數據已經過期,同時也無法修改 URL 上攜帶的數據指紋令牌。
但是其實我們是可以通過 App 和設備的一些固有信息,作為 URL 的參數傳遞,以此來刷新數據。
例如這里 /app/main 獲取主頁的數據,這里將當前 App 的版本號當參數拼接在 URL 的后面,以此方式來強制不同的版本,刷新不同的數據。避免剛升級上來的 App,還在使用舊版本的數據。
這個例子中,版本號只是其中一個維度,如果有必要,還可以傳遞其他維度的信息,例如當前網絡狀態,當前用戶 id 等等。
五、小結到這里我們基本上把 HTTP 的緩存所有相關的內容都講了一遍,這里簡單總結一下。
HTTP 緩存依賴 URL 做唯一標識,不同的 URL 使用不同的緩存。
Cache-Control 可以控制緩存策略,共有或者私有、緩存超時時長等。
通過 ETag 來標記數據指紋令牌,以此來確定響應數據是否更新。
應該為每個響應資源提供對應的緩存策略。
如果需要廢棄之前的緩存,可以利用修改請求 URL 的方式,將數據指紋令牌追加在 URL 之后,以此來更新數據。
關于 HTTP 緩存,你還有什么更好的想法,可以在留言區討論。
參考資料:
《HTTP緩存》:http://t.cn/RL1NI8P
《基于緩存策略三要素分解法》
公眾號后臺回復成長『成長』,將會得到我準備的學習資料,也能回復『加群』,一起學習進步;你還能回復『提問』,向我發起提問。
推薦閱讀:
小程序 UI 布局指南(一)
程序員的密碼管理之道
手動刷新 MediaStore,保存的圖片立即出現在相冊中
偽代碼、幽默和 Google 的藝術!
漫畫:App 防止 Fiddler 抓包小技巧!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/62004.html
摘要:讓你收獲滿滿碼個蛋從年月日推送第篇文章一年過去了已累積推文近篇文章,本文為年度精選,共計篇,按照類別整理便于讀者主題閱讀。本篇文章是今年的最后一篇技術文章,為了讓大家在家也能好好學習,特此花了幾個小時整理了這些文章。 showImg(https://segmentfault.com/img/remote/1460000013241596); 讓你收獲滿滿! 碼個蛋從2017年02月20...
摘要:歡迎來我的個人站點性能優化其他優化瀏覽器關鍵渲染路徑開啟性能優化之旅高性能滾動及頁面渲染優化理論寫法對壓縮率的影響唯快不破應用的個優化步驟進階鵝廠大神用直出實現網頁瞬開緩存網頁性能管理詳解寫給后端程序員的緩存原理介紹年底補課緩存機制優化動 歡迎來我的個人站點 性能優化 其他 優化瀏覽器關鍵渲染路徑 - 開啟性能優化之旅 高性能滾動 scroll 及頁面渲染優化 理論 | HTML寫法...
閱讀 1421·2021-10-11 11:12
閱讀 3252·2021-09-30 09:46
閱讀 1638·2021-07-28 00:14
閱讀 3141·2019-08-30 13:49
閱讀 2589·2019-08-29 11:27
閱讀 3239·2019-08-26 11:52
閱讀 606·2019-08-23 18:14
閱讀 3441·2019-08-23 16:27