【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

小生方勤發表於2019-04-01

前言

快取是一項用來提高網站效能不可或缺的技術,利用這項技術可以很好地提高 web 的效能。 快取可以很有效地降低網路的時延,同時也會減少大量請求對於伺服器的壓力。 大家在繼續看下去之前可以先思考一下 “從輸入 URL 到頁面載入完成的過程中都發生了什麼事情”。你現在所看到的其實就是這個熱點問題的一個變種問題。

不過這個問題對快取會有一個更詳盡的解釋。我相信你看完這篇文章後對快取會有一個全新的認識,如果沒有那就再看一遍。

入題

在這篇文章,我會詳盡的描述從輸入 URL 到展現涉及到的快取環節,不過由於本人知識有限,很可能有某些隱藏的快取機制在下遺漏了,還請大佬不吝賜教。

在講“從輸入 URL 到展現涉及到的快取環節”之前,我們先了解下快取的優點:

快取的幾個優點

  1. 減少冗餘的資料傳輸,可節省流量
  2. 緩解頻寬瓶頸問題,可更快載入頁面
  3. 緩解瞬間擁塞,可緩解原始伺服器的壓力
  4. 降低距離延時,加快響應速度

目錄

  1. 位址列網址快取
  2. 檢查 HSTS 預載入列表
  3. DNS 快取
  4. ARP(地址解析協議)快取
  5. TCP 傳送緩衝區 & 接收緩衝區
  6. HTTP 請求快取( CDN 節點快取、代理伺服器快取、瀏覽器快取、後端動態計算結果快取等 )

接下來我們進入正題(帶著答案,口味更佳):

一、位址列網址快取

輸入 url 後遇到的第一個快取環節就是位址列網址快取。

但我們輸入一個常用的網址時,經常會有這樣的情況,我們只是輸入了幾個字母,瀏覽器就自動補全了該網址。如下圖:我只輸入 j,就自動給我補全了 juejin.im

這就是位址列網址快取

當我們使用這個自動補全的網址時,你會發現請求的相關的靜態資源也是從快取中取得的。

【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

注意:不論什麼時候,我們獲取的主頁面資源 timeline, 都應該是重新請求伺服器而獲得的,不可以使用本地瀏覽器的快取。至於為什麼?你看到靜態資原始檔名的 hash 值你就應該清楚了。

【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

可以在 Chrome 的位址列中輸入 Chrome://cache 檢視快取的資訊

轉換非 ASCII 的 Unicode 字元

瀏覽器檢查輸入是否含有不是 a-z,A-Z,0-9, - 或者 . 的字元;如果有的話,瀏覽器會對主機名部分使用 Punycode 編碼

二、 檢查 HSTS 預載入列表

HSTS( HTTP Strict Transport Security )國際網際網路工程組織 IETE 正在推行一種新的 Web 安全協議,作用是強制客戶端(如瀏覽器)使用 HTTPS 與伺服器建立連線。

採用 HSTS 後:支援這個協議的瀏覽器,在輸入 URL 後會檢查自帶的 HSTS 預載入列表(這個列表裡包含了那些請求瀏覽器只使用 HTTPS 進行連線的域名),若網站在這個列表裡,瀏覽器會使用 HTTPS 協議並且返回碼為 307。而不支援 HSTS 的瀏覽器訪問我們的網站,則不會產生跳轉,從而提高了相容性。這個機制對於不支援 HTTPS 的搜尋引擎來說是非常友好的!

如掘金輸入 http://juejin.im/timeline 會跳轉到 https://juejin.im/timeline:

【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)
【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

檢視 HSTS 預載入列表是否存在你想訪問的域名你可以在輸入 qqbrowser://net-internals/#hsts,若存在會返回資訊:

【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

三、DNS 快取

但你輸入 juejin.im 按下回車後,就開始對 juejin.im 進行域名解析。域名解析最少涉及了三個地方的快取:

  1. 瀏覽器的 DNS 快取
  2. 作業系統中的 DNS 快取
  3. 索作業系統的 hosts 檔案(可手動寫入的快取)

域名解析的具體過程

【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

  1. 瀏覽器搜尋自己的 DNS 快取(瀏覽器維護一張域名與 IP 地址的對應表);如果沒有命中,進入下一步;
  2. 搜尋作業系統中的 DNS 快取;如果沒有命中,進入下一步;
  3. 搜尋作業系統的 hosts 檔案( Windows 環境下,維護一張域名與 IP 地址的對應表);如果沒有命中,進入下一步;
  1. 作業系統將域名傳送至 LDNS (本地區域名伺服器),LDNS 查詢自己的 DNS 快取(一般命中率在 80% 左右),查詢成功則返回結果,失敗則發起一個迭代 DNS 解析請求:
  2. LDNS向 Root Name Server(根域名伺服器,如com、net、im 等的頂級域名伺服器的地址)發起請求,此處,Root Name Server 返回 im 域的頂級域名伺服器的地址;
  3. LDNS 向 im 域的頂級域名伺服器發起請求,返回 juejin.im 域名伺服器地址;
  4. LDNS 向 juejin.im 域名伺服器發起請求,得到 juejin.im 的 IP 地址;
  5. LDNS 將得到的 IP 地址返回給作業系統,同時自己也將 IP 地址快取起來;作業系統將 IP 地址返回給瀏覽器,同時自己也將 IP 地址快取起來。

DNS Prefetch

即 DNS 預獲取,是前端優化的一部分。一般來說,在前端優化中與 DNS 有關的有兩點:

  1. 減少 DNS 的請求次數
  2. 進行 DNS 預獲取

典型的一次 DNS 解析需要耗費 20-120 毫秒,減少DNS解析時間和次數是個很好的優化方式。DNS Prefetching 是讓具有此屬性的域名不需要使用者點選連結就在後臺解析,而域名解析和內容載入是序列的網路操作,所以這個方式能減少使用者的等待時間,提升使用者體驗。

你可以通過 chrome://net-internals/#dns 查詢目前系統中的 DNS 快取和 Chrome 中使用的情況。

提個問題

問:瀏覽器 DNS 快取的時間一般不會太長,一分鐘左右。為什麼快取不設定較長時間呢?

答:雖然 DNS 快取可以提高獲取 DNS 的速度,但快取時間過長也會影響 DNS 在 IP 變更時不能及時解析到最新的 IP。

四、ARP(地址解析協議)快取

ARP 是一種用以解釋地址的協議,根據通訊方的 IP 地址就可以反查出對應方的 MAC 地址。

ARP 快取是個用來儲存 IP 地址和 MAC 地址的緩衝區,其本質就是一個 IP 地址與 MAC 地址的對應表,表中每一個條目分別記錄了其他主機的 IP 地址和對應的 MAC 地址。

當地址解析協議被詢問一個已知 IP 地址節點的 MAC 地址時,先在 AR 快取中檢視,若存在,就直接返回與之對應的MAC地址;若不存在,才傳送 ARP 請求查詢。

具體的 ARP 請求查詢感興趣的同學可以自行研究。

五、TCP 傳送緩衝區 & 接收緩衝區

建立 TCP 連線這一步也涉及到快取 —— 用來臨時存放雙方通訊的資料,保證通訊資料不會丟包

每個 TCP 連線在核心中都有一個傳送緩衝區和接收緩衝區,TCP 的全雙工的工作模式以及 TCP 的流量(擁塞)控制便是依賴於這兩個獨立的 buffer 以及 buffer 的填充狀態。

傳送緩衝區

傳送緩衝區存放的是 send() 方法從應用緩衝區拷貝過來的資料。

核心基本上是按照 MSS(Maximum Segment Size,最大報文段長度) 從緩衝區中取資料傳送出去,當緩衝區中資料小於 MSS,則將剩餘資料全部傳送出去。TCP 的傳送緩衝區必須為已傳送的資料保留一個副本,直到它被對端確認為止,才能從緩衝區中刪掉已確認的資料。

接收緩衝區

接收緩衝區被 TCP 用來儲存接收到的資料,直到應用程式來讀取。

接收緩衝區把資料快取入核心,等待 recv() 方法讀取,recv() 方法所做的工作,就是把核心緩衝區中的資料拷貝到應用層使用者的 buffer 裡面,拷貝後就刪掉已確認的資料。

流控制(Flow Control)

A mechanism to prevent a TCP sender from overwhelming a TCP receiver.
TCP 流控制主要用於匹配傳送端和接收端的速度,即根據接收端當前的接收能力來調整傳送端的傳送速度。

由於傳送速度可能大於接收速度,接收端的應用程式未能及時從接收緩衝區讀取資料,接收緩衝區不夠大不能快取所有接收到的報文等原因,TCP接收端的接收緩衝區很快就會被塞滿;從而導致不能接收後續的資料,傳送端此後傳送資料是無效的,因此需要流控制。

TCP 的快取就講到這裡,感興趣的可以自己翻閱資料。

六、HTTP 請求快取( CDN 節點快取、代理伺服器快取、瀏覽器快取、後端動態計算結果快取等 )

在建立了 TCP 連線之後,就開始 HTTP 請求了;而 HTTP 快取是優化效能不可忽視的一部分,這一部分我會著重講解。

再講具體過程之前,我再講一遍強快取和協商快取。

強快取 ( Cache-Control 和 Expires )

強快取主要是採用響應頭中的 Cache-ControlExpires 兩個欄位進行控制的。

其中 ExpiresHTTP 1.0 中定義的,它指定了一個絕對的過期時期。而 Cache-ControlHTTP 1.1 時出現的快取控制欄位。 由於 ExpiresHTTP1.0 時代的產物,因此設計之初就存在著一些缺陷,如果本地時間和伺服器時間相差太大,就會導致快取錯亂。

這兩個欄位同時使用的時候 Cache-Control 的優先順序會更高一點。

這兩個欄位的效果是類似的,客戶端都會通過對比本地時間和伺服器返回的生存時間來檢測快取是否可用。如果快取沒有超出它的生存時間,客戶端就會直接採用本地的快取。如果生存日期已經過了,這個快取也就宣告失效。接著客戶端將再次與伺服器進行通訊來驗證這個快取是否需要更新

請求頭中使用 Cache-Control 時,它可選的值有:

指令 說明
no-cache 使用代理伺服器的快取之前提交原始伺服器驗證,驗證通過才能使用
no-store 在客戶端或是代理伺服器都不快取請求或響應的任何內容
max-age=[秒] 告知伺服器客戶端可接受資源的存在最大時間
max-stale(=[秒]) 可接受(代理伺服器快取的)過期資源,引數可省略
min-fresh=[秒] 可接受(代理伺服器快取的)資源更新時間小於指定時間
no-transform 代理伺服器不可以更改媒體型別
only-if-cached 客戶端只接受已快取的響應,若快取不命中,則返回 504 錯誤
cache-extension 自定義擴充套件值,若伺服器不知別該指令,就直接忽略

響應頭中使用 Cache-Control 時,它可選的值有:

指令 說明
public 表明該資源可以給多個使用者使用
private(= name) 該資源是私有資源,指定的使用者可以使用的快取
no-cache 強制每次請求直接傳送給源伺服器,而不經過本地快取版本的校驗。
no-store 在客戶端或是代理伺服器都不快取請求或響應的任何內容
no-transform 代理伺服器不可以更改媒體型別
must-revalidate 可快取但必須再向源伺服器進行請求確認
proxy-revalidate 要求快取伺服器返回快取的時候向源伺服器進行請求確認
max-age=[秒] 告知客戶端該資源在規定時間內是新鮮的,無需向伺服器確認
s-maxage=[秒] 告知快取服務該資源在規定時間內是新鮮的,無需向伺服器確認
cache-extension 自定義擴充套件值,若伺服器不識別該指令,就直接忽略

可快取性

  1. public:響應可以被任何物件(客戶端、代理伺服器等)快取
  2. private:只能被單個使用者快取,不能作為共享快取
  3. no-cache:使用快取副本之前,需要將請求提交給原始伺服器進行驗證,驗證通過才可以使用
  4. only-if-cached:客戶端只接受已快取的響應,並且不向原始伺服器檢查是否有更新的拷貝

到期

  1. max-age=<seconds>:快取儲存的最大週期,超過這個時間快取被認為過期(單位秒)。與 Expires 相反,時間是相對於請求的時間
  2. s-maxage=<seconds>:覆蓋 max-age 或者 Expires 頭,但是僅適用於共享快取(比如各個代理),並且私有快取中它被忽略
  3. max-stale[=<seconds>]:表明客戶端願意接收一個已經過期的資源。可選的設定一個時間(單位秒),表示響應不能超過的過時時間
  4. min-fresh=<seconds>:表示客戶端希望在指定的時間內獲取最新的響應

重新驗證和重新載入

  1. must-revalidate:快取必須在使用之前驗證舊資源的狀態,並且不可使用過期資源。
  2. proxy-revalidate:與 must-revalidate 作用相同,但它僅適用於共享快取(例如代理),並被私有快取忽略

其他

  1. no-store:徹底得禁用緩衝,本地和代理伺服器都不緩衝,每次都從伺服器獲取
  2. no-transform:不得對資源進行轉換或轉變。Content-Encoding, Content-Range, Content-TypeHTTP 頭不能由代理修改。

協商快取 ( Last-Modified 和 Etag )

協商快取機制下,瀏覽器需要向伺服器去詢問快取的相關資訊,進而判斷是重新發起請求還是從本地獲取快取的資源。如果服務端提示快取資源未改動( Not Modified ),資源會被重定向到瀏覽器快取,這種情況下網路請求對應的狀態碼是 304

**Last-Modified 和 If-Modified-Since **

基於資源在伺服器修改時間而驗證快取的過期機制

當客戶端再次請求該資源的時候,會在其請求頭上附帶上 If-Modified-Since 欄位(值就是第一次獲取請求資源時響應頭中返回的 Last-Modified 值)。如果修改時間未改變則表明資源未過期,命中快取,伺服器就直接返回 304 狀態碼,客戶端直接使用本地的資源。否則,伺服器重新傳送響應資源,從而保證資源的有效性。

Etag 和 If-None-Match

基於資源校驗碼(一般為md5值)而驗證快取的過期機制

當客戶端再次請求該資源的時候,會在其請求頭上附帶上 If-None-Match 欄位(值就是第一次獲取請求資源時響應頭中返回的 Etag 值),其值與伺服器端資原始檔的驗證碼進行對比,如果匹配成功直接返回 304 狀態碼,從瀏覽器本地快取取資原始檔。如果不匹配,伺服器會把新的驗證碼放在請求頭的 Etag 欄位中,並且以 200 狀態碼返回資源。

需要注意的是當響應頭中同時存在 EtagLast-Modified 的時候,會先對 Etag 進行比對,隨後才是 Last-Modified

Etag 的問題
相同的資源,在兩臺伺服器產生的 Etag 是不是相同的,所以對於使用伺服器叢集來處理請求的網站來說,Etag 的匹配概率會大幅降低。所在在這種情況下,使用 Etag 來處理快取,反而會有更大的開銷。

靜態資源和動態資源的請求過程解析

靜態資源
第一次請求肯定是從伺服器請求過來的資源,這個沒有什麼疑問,我們先看看第一次請求的響應頭的內容:

【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

我們發現第一次的響應頭中包含可強快取的相關欄位 cache-control ,同時也包含了協商快取的相關欄位 etaglast-modified;

當強快取和協商快取欄位同時存在時會進行以下步驟來請求資源:

  1. 強快取和協商快取同時存在,如果強快取還在有效期內則直接使用快取;如果強快取不在有效期,協商快取生效。
    即:強快取優先順序 > 協商快取優先順序

  2. 強快取的 expirescache-control 同時存在時,cache-control 會覆蓋 expires 的效果,expires 無論有沒有過期,都無效。
    即:cache-control 優先順序 > expires 優先順序。

  3. 協商快取的 EtagLast-Modified 同時存在時,會先對 Etag 進行比對,隨後才是 Last-Modified。 即:ETag 優先順序 > Last-Modified 優先順序。

第二次請求該資源的時候,就直接是從快取中讀取的:

【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

其實我們第一次獲取的資源極有可能是從 CDN 節點的快取中獲取的,也很有可能是從中間代理伺服器(nginx,node 等)的快取中讀取的;其中的好處不言而喻。

動態資源

由於動態資源的返回結果不一致,所以這個我們肯定不會在瀏覽器(中間代理伺服器)快取動態的結果。

不過這裡我們可以在後端快取一些重複率比較高的相關的計算結果。

如:這裡有 60 只股票,使用者可以選擇其中幾隻股票作為自己的股票投資池。使用者選擇完股票後提交,會通過相關的演算法計算其預期收益效果等指標。我們知道每次計算的時間可能會比較久,所以在這步我們可以在後端將可能的組合結果先計算好快取起來,當我們請求的時候就後端就可以直接返回已經計算好的結果給前端。至於計算結果的快取時間也就完全由伺服器控制了。

關於動態資源一般前端是不做快取的。

後端快取主要通過保留資料庫連線,儲存處理結果等方式縮短處理時間,儘快響應客戶端請求。

成功獲得資源後就開始客戶端渲染,接下來的相關解析過程這裡我就不講了,有興趣的同學可以留言交流。

前端詞典系列

《前端詞典》這個系列會持續更新,每一期我都會講一個出現頻率較高的知識點。希望大家在閱讀的過程當中可以斧正文中出現不嚴謹或是錯誤的地方,本人將不勝感激;若通過本系列而有所得,本人亦將不勝欣喜。

如果你覺得我的文章寫的還不錯,可以關注我的微信公眾號,公眾號裡會提前劇透呦。

【前端詞典】從輸入 URL 到展現涉及哪些快取環節(非常詳細)

你也可以新增我的微信 wqhhsd, 歡迎交流。

傳送門

  1. 【前端詞典】和媳婦講代理後的意外收穫
  2. 【前端詞典】滾動穿透問題的解決方案
  3. 【前端詞典】繼承(一) - 面試官問的你都會嗎?
  4. 【前端詞典】繼承(二) - 回的八種寫法·面試必問
  5. 【前端詞典】進階必備的網路基礎(上)
  6. 【前端詞典】進階必備的網路基礎(下)
  7. 【前端詞典】F5 同 Ctrl+F5 的區別你可瞭解
  8. 【前端詞典】實現 Canvas 下雪背景引發的效能思考

相關文章