前端性能優化資料整理

前端性能優化資料整理

頁面性能差的直接后果是用戶需要等待,而等待,尤其是不確定要多長時間的等待會給用戶帶來焦慮,為了盡早的結束這種焦慮,除非訪問網頁是剛需,用戶通常會選擇直接關閉頁面。從實際數據來看,性能差是頁面高跳出率的重要原因之一。以下內容部分來自網絡并且結合個人理解持續更新


瀏覽器的基本知識以及基礎渲染原理

1.瀏覽器應該有的功能

  • 網絡:

瀏覽器通過網絡模塊來下載各式各樣的資源,例如html文本;javascript代碼;樣式表;圖片;音視頻文件等。
網絡部分本質上十分重要,因為它耗時長,而且需要安全訪問互聯網上的資源。

  • 資源管理:

從網絡下載,或者本地獲取到的資源需要有高效的機制來管理它們。
例如如何避免重復下載,資源如何緩存等

  • 網頁瀏覽:

這是瀏覽器的核心也是最基本的功能,最重要的功能。
如何將資源轉變為可視化的結果。

其他功能

  • 多頁面管理
  • 插件與管理
  • 賬戶和同步
  • 安全機制
  • 開發者工具

瀏覽器的主要功能可以總結為:

將用戶輸入的url轉變成可視化的圖像
1.從url到dom樹
2.從dom樹到可視化圖像
這兩個過程之間的關系并沒有那么明確,我們可以統稱這兩個過程為頁面的渲染

2.瀏覽器的渲染引擎

在瀏覽器中有一個最重要的模塊,它主要的作用是將頁面轉變為可視化的圖像結果。
這個模塊就是瀏覽器內核,通常它也被稱為渲染引擎。

  • IE --> Trident
  • Safari --> WebKit
    • WebKit本身主要是由兩個引擎構成的,
      一個正是渲染引擎“WebCore”,
      另一個則是javascript解釋引擎“JSCore”,
      它們均是從KDE的渲染引擎KHTML及javascript解釋引擎KJS衍生而來。
  • Chrome --> WebKit的分支引擎 --> Blink
    • 在13年發布的Chrome 28.0.1469.0版本開始,Chrome放棄Chromium引擎轉
      而使用最新的Blink引擎(基于WebKit2——蘋果公司于2010年推出的新的WebKit引擎),
      Blink對比上一代的引擎精簡了代碼、改善了DOM框架,也提升了安全性。
  • Opera
    • 舊版Opera 4至6版本 --> Elektra排版引擎
    • Opera7.0 --> Presto渲染引擎
    • Opera在2013年2月宣布放棄Presto:
    • 采用Chromium引擎;
    • 又轉為Blink引擎;
  • Firefox --> Gecko

3.進程&線程

  • 進程: 程序的一次執行, 它占有一片獨有的內存空間.是操作系統執行的基本單元。

一個進程中至少有一個運行的線程: 主線程, 進程啟動后自動創建
一個進程中也可以同時運行多個線程, 我們會說程序是多線程運行的
一個進程內的數據可以供其中的多個線程直接共享,多個進程之間的數據是不能直接共享的

  • 線程:是進程內的一個獨立執行單元,是CPU調度的最小單元。程序運行的基本單元

線程池(thread pool): 保存多個線程對象的容器, 實現線程對象的反復利用

JS引擎是單線程運行的(異步與同步)

4.現代瀏覽器的多進程多線程模型

  • 1.不堪回首的過去:

    • 當你通過瀏覽器打開很多頁面的時候,如果其中一個頁面不響應了或者崩潰了,
    • 那么隨之而來的將會是更不幸的事情,你開打的所有頁面都會得不到響應,
    • 最讓人不能忍受的是,其中的一些頁面可能還包含了未保存或者未發送的信息
  • 2.瀏覽器產商如何解決

    • 采用多進程模型,該模型可以帶來的好處
    • ①.避免因單個頁面的不響應或者崩潰影響整個瀏覽器的穩定性
    • ②.當第三方插件崩潰時,也不會影響整個瀏覽器的穩定性
    • ③.安全
  • 3.瀏覽器到底有些什么進程

    • ①.Browser進程:
      瀏覽器的主進程,負責瀏覽器界面的顯示,和各個頁面的管理,
      瀏覽器中所有其他類型進程的祖先,負責其他進程的的創建和銷毀
      它有且只有一個!!!!!
    • ②.Renderer進程:
      網頁渲染進程,負責頁面的渲染,可以有多個
      當然渲染進程的數量不一定等于你開打網頁的個數
    • ③.NPAPI插件進程
    • ④.Pepper插件進程
    • ⑤.GPU進程
  • 移動設備的瀏覽器可能不太一樣:

    • Android不支持插件,所以就沒有插件進程
    • GUP演化成了Browser進程的一個線程
    • Renderer進程演化成了操作系統的一個服務進程,它仍然是獨立的
  • 4.每個進程內部又有很多線程

    • 多線程的目的主要是保持用戶界面的高度響應
    • 例如:為了不讓Browser進程的UI線程被其他耗時的操作(數據庫讀寫,本地文件讀寫)所阻塞,
    • 那么我們就把這些操作放到分線程中去處理
    • 在Renderer進程中,為了不讓其他操作阻止渲染線程的高速執行,我們通常會將渲染過程管線化,
      利用計算機的多核優勢,讓渲染的不同階段在不同的線程中執行

管線化一次處理多個請求,不需要一個接一個地等待響應


渲染引擎

1.主要引擎

一個渲染引擎主要包括:HTML解析器,CSS解析器,布局layout模塊,javascript引擎,繪圖模塊

  • HTML解析器:
    • 解釋HTML文檔的解析器,主要作用是將HTML文本解釋成DOM樹。
  • CSS解析器:
    • 級聯樣式表的解析器,它的作用是為DOM中的各個元素對象計算出樣式信息,為布局提供基礎設施
  • 布局(layout):
    • 在DOM創建之后,Webkit需要將其中的元素對象同樣式信息結合起來,
      計算他們的大小位置等布局信息,形成一個能表達這所有信息的內部表示模型
  • Javascript引擎:
    • 使用Javascript代碼可以修改網頁的內容,也能修改css的信息,javascript引擎能夠解釋javascript代碼
      并通過DOM接口和CSSOM接口來修改網頁內容和樣式信息,從而改變渲染的結果。
  • 繪圖模塊:
    • 使用圖形庫將布局計算后的各個網頁的節點繪制成圖像結果

以上這些模塊依賴很多其他的基礎模塊,包括要使用到網絡 存儲 2D/3D圖像 音頻視頻解碼器 和 圖片解碼器。
所以渲染引擎中還會包括如何使用這些依賴模塊的部分。

2.渲染過程

1.網頁URL到構建DOM樹的整個過程

  • a. 當用戶輸入URL的時候,Webkit調用資源加載器加載URL對應的網頁
  • b. 加載器依賴網絡模塊建立連接,發送請求并接收答復
  • c. Webkit接收到各種網頁或者資源的數據,其中某些資源可能是同步的或異步獲取的
  • d. 網頁被交給HTML解析器轉變一系列的詞語(Token)
  • e. 解析器根據詞語構建節點(node),形成DOM樹
  • f. 如果節點需要依賴于其他資源,
    • 例如js css 圖片 視頻等,調用資源加載器來加載他們,但是這些都是異步的,不會阻礙dom樹的繼續創建;
    • 也就是說資源加載是并發的,但這個并發度受到域名的限制,所有一般我們會在一個項目中配多個CDN地址
    • 來盡量使瀏覽器處于一個并發飽滿的狀態。順序執行 并發加載
    • 如果資源是css的話
      • 調用CSS解析器解釋將CSS解釋成內部表示結構( CSSDOM)
    • 如果資源是javascript的話
      • 調用Javascript引擎解釋并執行,
  • g. 阻塞(下面單獨說明)

在上述的過程中,網頁在加載和渲染過程中會發出“DOMContentloaded”和“onload”事件
分別在DOM樹構建完成之后,以及DOM樹構建完并且網頁所依賴的資源都加載完之后發生、
因為某些資源的加載并不會阻礙DOM樹的創建,所以這兩個事假多數是不同時發生的

2.阻塞

  • css阻塞
    • css 在head中引入會阻塞頁面的渲染
    • 為什么?style被html解析器當做html元素解析了
    • 避免閃屏現象:使用link的外聯樣式表
    • css 阻塞js的執行,不阻塞js等其他資源的加載
    • 為什么?↓

腳本在文檔解析階段會請求樣式信息。如果當時還沒有加載和解析樣式,
腳本就會獲得錯誤的回復,這樣顯然會產生很多問題。這看上去是一個非典型案例,
但事實上非常普遍。Firefox 在樣式表加載和解析的過程中,會禁止所有腳本。
而對于 WebKit 而言,僅當腳本嘗試訪問的樣式屬性可能受尚未加載的樣式表影響時,它才會禁止該腳本。

  • js阻塞

    • 直接引入的js會阻塞頁面的渲染
    • 為什么?Javascript代碼可能會修改DOM樹的結構
    • js順序執行,阻塞后續js邏輯的執行,不阻塞js等其他資源的加載
    • 為什么?維護依賴關系
  • 預解析

WebKit 和 Firefox 都進行了這項優化。在執行js腳本時,其他線程會解析文檔的其余部分,
找出并加載需要通過網絡加載的其他資源。通過這種方式,資源可以在并行連接上加載,
從而提高總體速度。請注意,預解析器不會修改 DOM 樹,而是將這項工作交由主解析器處理;
預解析器只會解析外部資源(例如外部腳本、樣式表和圖片)的引用。

3.從DOM樹到可視化圖像

  • 1.CSS文件被CSS解析器解釋成內部表示結構( CSSDOM)
  • 2.CSS解析器工作完成之后,在DOM樹上附加解釋后的樣式信息,這就是RenderObject樹
  • 3.RenderObject在創建的同時,Webkit會根據網頁的結構創建RenderLayer,同時構建一個繪圖上下文
  • 4.根據繪圖上下文生成最終的圖像(這一過程需要依賴圖形庫)

上面介紹的是一個完整的渲染過程,但現代網頁很多都是動態的,這意味著在渲染完成之后,由于網頁的動畫或者用戶的交互,
瀏覽器其實一直在不停地重復執行渲染過程。(重繪重排),以上的數字表示的是基本順序,這不是嚴格一致的,
這個過程可能重復也可能交叉


圖層&重繪重排

1.css圖層

瀏覽器在渲染一個頁面時,會將頁面分為很多個圖層,圖層有大有小,每個圖層上有一個或多個節點。

在渲染DOM的時候,瀏覽器所做的工作實際上是:

  • 1.獲取DOM后分割為多個圖層
  • 2.對每個圖層的節點計算樣式結果 (Recalculate style --> 樣式重計算)
  • 3.為每個節點生成圖形和位置 (Layout --> 重排,回流)
  • 4.將每個節點繪制填充到圖層位圖中 (Paint --> 重繪)
  • 5.圖層作為紋理上傳至GPU
  • 6.符合多個圖層到頁面上生成最終屏幕圖像 (Composite Layers --> 圖層重組)

2.css圖層創建的條件(以Chrome為基準)

  • 1.擁有具有3D變換的CSS屬性
  • 2.使用加速視頻解碼的video節點
  • 3.canvas節點
  • 4.CSS3動畫的節點
  • 5.擁有CSS加速屬性的元素(will-change)
  • 6.元素有一個z-index較低且包含一個復合層的兄弟元素(換句話說就是該元素在復合層上面渲染)
    你在圖層上渲染,有一個z-index較低兄弟元素,而且這個兄弟元素本身就有一個圖層則該元素會影響其他兄弟元素產生圖層,但是這不是我們想要的,給這個要開啟圖層的元素比較高的層級就不會影響其他元素

關于will-change(以下內容來自MDN)

  • 不要將 will-change 應用到太多元素上:瀏覽器已經盡力嘗試去優化一切可以優化的東西了。有一些更強力的優化,如果與 will-change 結合在一起的話,有可能會消耗很多機器資源,如果過度使用的話,可能導致頁面響應緩慢或者消耗非常多的資源。

  • 有節制地使用:通常,當元素恢復到初始狀態時,瀏覽器會丟棄掉之前做的優化工作。但是如果直接在樣式表中顯式聲明了will-change屬性,則表示目標元素可能會經常變化,瀏覽器會將優化工作保存得比之前更久。所以最佳實踐是當元素變化之前和之后通過腳本來切換 will-change 的值。

  • 不要過早應用 will-change 優化:如果你的頁面在性能方面沒什么問題,則不要添加 will-change 屬性來榨取一丁點的速度。 will-change 的設計初衷是作為最后的優化手段,用來嘗試解決現有的性能問題。它不應該被用來預防性能問題。過度使用 will-change 會導致大量的內存占用,并會導致更復雜的渲染過程,因為瀏覽器會試圖準備可能存在的變化過程。這會導致更嚴重的性能問題。
  • 給它足夠的工作時間:這個屬性是用來讓頁面開發者告知瀏覽器哪些屬性可能會變化的。然后瀏覽器可以選擇在變化發生前提前去做一些優化工作。所以給瀏覽器一點時間去真正做這些優化工作是非常重要的。使用時需要嘗試去找到一些方法提前一定時間獲知元素可能發生的變化,然后為它加上 will-change 屬性。

3.重繪(Repaint)

重繪是一個元素外觀的改變所觸發的瀏覽器行為,例如改變outline、背景色等屬性。瀏覽器會根據元素的新屬性重新繪制,使元素呈現新的外觀。重繪不會帶來重新布局,所以并不一定伴隨重排。

  • 需要注意的是,如果圖層中某個元素需要重繪,那么整個圖層都需要重繪。
  • 比如一個圖層包含很多節點,其中有個gif圖,gif圖的每一幀,都會重回整個圖層的其他節點,然后生成最終的圖層位圖。
  • 所以這需要通過特殊的方式來強制gif圖屬于自己一個圖層(translateZ(0)或者translate3d(0,0,0)
  • CSS3的動畫也是一樣(好在絕大部分情況瀏覽器自己會為CSS3動畫的節點創建圖層)

4.重排(Reflow 回流)

渲染對象在創建完成并添加到渲染樹時,并不包含位置和大小信息。計算這些值的過程稱為布局或重排

  • "重繪"不一定需要"重排",比如改變某個網頁元素的顏色,就只會觸發"重繪",不會觸發"重排",因為布局沒有改變。
  • 但是,"重排"必然導致"重繪",比如改變一個網頁元素的位置,就會同時觸發"重排"和"重繪",因為布局改變了。

5.觸發重繪的屬性

基本樣式 背景樣式 盒模型相關
color background outline-color
border-style background-image outline
border-radius background-position outline-style
visibility background-repeat outline-width
text-decoration background-size box-shado

5.觸發重排的屬性

盒子模型相關屬性 定位屬性及浮動 改變節點內部文字結構
width top text-align
heigh bottom overflow-y
padding left font-weight
margin righ overflow
display position font-family
border-width float line-height
border clear vertival-align
min-height white-space

6.常見的觸發重排的操作

重排的成本比重繪的成本高得多的多。DOM Tree 里的每個結點都會有重排方法,
一個結點的重排很有可能導致子結點,甚至父點以及同級結點的 重排。在一些高性能的電腦上也許還沒什么,
但是如果重排發生在手機上,那么這個過程是非常痛苦和耗電的。

下面這些動作有很大可能會是成本比較高的。

  • 當你增加、刪除、修改 DOM 結點時,會導致重繪重排
  • 當你移動 DOM 的位置
  • 當你修改 CSS 樣式的時候。
  • 當你 Resize 窗口的時候(移動端沒有這個問題)
  • 當你修改網頁的默認字體時。
  • 獲取某些屬性時(width,height...)

!注:display:none 會觸發 重排,而 visibility:hidden 只會觸發 重排,因為沒有發生位置變化。

7.優化

如果我們需要使得動畫或其他節點渲染的性能提高,需要做的就是減少瀏覽器在運行時所需要做的工作(減少1234中的步驟)

  1. 計算需要被加載到節點上的樣式結果(Recalculate style--樣式重計算)
  2. 為每個節點生成圖形和位置(Layout--回流和重布局)
  3. 將每個節點填充到圖層中(Paint Setup和Paint--重繪)
  4. 組合圖層到頁面上(Composite Layers--圖層重組)
  • 1.元素位置移動變換時盡量使用CSS3的transform來代替對top left等的操作
    • 變換(transform)和透明度(opacity)的改變僅僅影響圖層的組合
  • 2.使用opacity來代替visibility
    • 透明度竟然不會觸發重繪?
    • 透明度的改變時,GPU在繪畫時只是簡單的降低之前已經畫好的紋理的alpha值來達到效果,并不需要整體的重繪。
    • !不過這個前提是這個被修改opacity本身必須是一個圖層,如果圖層下還有其他節點,GPU也會將他們透明化
  • 3.不要使用table布局
    • table-cell
  • 4.將多次改變樣式屬性的操作合并成一次操作
    • 不要一條一條地修改DOM的樣式,預先定義好class,然后修改DOM的className
  • 5.將DOM離線后再修改
    • 由于display屬性為none的元素不在渲染樹中,對隱藏的元素操作不會引發其他元素的重排。
    • 如果要對一個元素進行復雜的操作時,可以先隱藏它,操作完成后再顯示。這樣只在隱藏和顯示時觸發2次重排。
  • 6.利用文檔碎片(document.createDocumentFragment())

DocumentFragment 節點不屬于文檔樹,繼承的 parentNode 屬性總是 null。不過它有一種特殊的行為,該行為使得它非常有用,即當請求把一個 DocumentFragment 節點插入文檔樹時,插入的不是DocumentFragment 自身,而是它的所有子孫節點。這使得 DocumentFragment 成了有用的占位符,暫時存放那些一次插入文檔的節點。它還有利于實現文檔的剪切、復制和粘貼操作,尤其是與 Range 接口一起使用時更是如此。

  • 7.不要把某些DOM節點的屬性值放在一個循環里當成循環的變量
    當你請求向瀏覽器請求一些 style信息的時候,就會讓瀏覽器flush隊列,比如:
    • 1.offsetTop, offsetLeft, offsetWidth, offsetHeight
    • 2.scrollTop/Left/Width/Height
    • 3.clientTop/Left/Width/Height
    • 4.width,height

當你請求上面的一些屬性的時候,瀏覽器為了給你最精確的值,需要flush隊列,
因為隊列中可能會有影響到這些值的操作。即使你獲取元素的布局和樣式信息跟最近發生或改變的布局信息無關,
瀏覽器都會強行刷新渲染隊列。

  • 8.動畫實現過程中,啟用GPU硬件加速
  • 9.為動畫元素新建圖層,提高動畫元素的z-index

8.使用requestAnimationFrame

window.requestAnimationFrame() 方法告訴瀏覽器您希望執行動畫并請求瀏覽器在下一次重繪之前調用指定的函數來更新動畫
該方法使用一個回調函數作為參數,這個回調函數會在瀏覽器重繪之前調用。

  • 回調函數會被傳入一個參數,DOMHighResTimeStamp,指示requestAnimationFrame() 開始觸發回調函數的當前時間

  • 返回值

    • 一個 long 整數,請求 ID ,是回調列表中唯一的標識。是個非零值,沒別的意義。
      你可以傳這個值給 window.cancelAnimationFrame() 以取消回調函數。
  • window.cancelAnimationFrame(requestID)

    • 取消一個先前通過調用window.requestAnimationFrame()方法添加到計劃中的動畫幀請求.
      requestID是先前調用window.requestAnimationFrame()方法時返回的ID
//基本使用
var timer;
btn.onclick = function(){
    myDiv.style.width = '0';
    cancelAnimationFrame(timer);
    timer = requestAnimationFrame(function fn(){
        if(parseInt(myDiv.style.width) < 500){
            myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
            myDiv.innerHTML =     parseInt(myDiv.style.width)/5 + '%';
            timer = requestAnimationFrame(fn);//遞歸調用
        }else{
            cancelAnimationFrame(timer);
        }    
    });
}

DNS&CDN

1.DNS域名解析服務

DNS服務是和HTTP協議一樣位于應用層的協議,它提供域名到IP地址之間的解析服務。

  • 計算機既可以被賦予IP地址,也可以被賦予主機名和域名。
  • 比如www.xxx.com用戶通常使用主機名或域名來訪問對方的計算機,而不是通過IP地址訪問,因為與IP地址的一組純數字相比,用域名來指定計算機名更符合人類的記憶習慣。
  • 但是要讓計算機去理解域名,相對而言就變得困難了,因為計算機更擅長處理一長串數字。
  • 為了解決上述問題,DNS服務應運而生。NDS協議提供通過域名查找IP,或逆向從IP地址反查域名的服務
chrome://dns/           :查看chrome瀏覽器上的DNS緩存
ipconfig /displaydns    :查看操作系統中的DNS緩存  

2.一次完整的請求在網絡層面前端需要關注什么?

  • DNS是否可以通過緩存來減少查詢IP地址的時間?
    • 可以的本地緩存會大大降低DNS解析事件
  • 網絡的請求過程是否走的是最近的網絡?
    • 網絡的物理距離會影響響應的效率
  • 是否可以少發幾次請求?
    • 合并依賴、精靈圖等等資源合并操作..
  • 請求體是否可以盡量的小?
    • 壓縮圖片,壓縮代碼等等資源壓縮操作

3.DNS是否可以通過緩存來減少查詢IP地址的時間?

  • DNS的查找是有開銷的,通常瀏覽器查找一個給定的主機名的IP地址需要花費20到120毫秒。
  • 在DNS查找完成之前,瀏覽器不能從主機那下載任何東西。
  • DNS是可以被緩存起來提高性能的,這種緩存可以發生在網絡運營商的服務器上,
    也可以發在本機所處的局域網中,甚至可以發生在本地的操作系統或瀏覽器中!!
  • 但是我們也知道服務器的IP地址是可變的,緩存會消耗內存,因此不管是哪個級別的
    緩存都應該周期性的清除一下。

DNS域名解析過程

  • 第1步,查找瀏覽器緩存。
    • 瀏覽器會檢查緩存中有沒有這個域名對應的解析過的IP地址,如果緩存中有,這個解析過程就將結束。
  • 第2步,查找系統緩存
    • 如果用戶的瀏覽器緩存中沒有,瀏覽器會查找操作系統緩存中是否有這個域名對應的DNS解析結果
  • 第3步,查找路由器緩存。
    • 如果系統緩存中也找不到,那么查詢請求就會發向路由器,它一般會有自己的DNS緩存。
  • 第4步,查找ISP DNS 緩存。(網絡運營商)

運氣實在不好,就只能查詢ISP DNS緩存服務器了。在我們的網絡配置中都會有"DNS服務器地址"這一項,
操作系統會把這個域名發送給這里設置的DNS,也就是本地區的域名服務器,這個專門的域名解析服務器性能都會很好
它們一般都會緩存域名解析結果,當然緩存時間是受域名的失效時間控制的。大約80%的域名解析都到這里就已經完成了,
所以ISP DNS主要承擔了域名的解析工作。

  • 第5步,遞歸搜索
    最無奈的情況發生了, 在前面都沒有辦法命中的DNS緩存的情況下
    • 1.本地 DNS服務器即將該請求轉發到互聯網上的根域
    • 2.根域將所要查詢域名中的頂級域(即blog.baidu.com中的com)的服務器IP地址返回到本地DNS。
    • 3.本地DNS根據返回的IP地址,再向頂級域(就是com域)發送請求。
    • 4.com域服務器再將域名中的二級域(即blog.baidu.com中的baidu)的IP地址返回給本地DNS。
    • 5.本地DNS再向二級域發送請求進行查詢。
    • 6.之后不斷重復這樣的過程直到本地DNS服務器得到最終的查詢結果,并返回到主機。這時候主機才能通過域名訪問該網站。

減少DNS查詢

  • 1.一個多資源的站點最好使用2到4個不一樣的主機來存放服務端資源。
    這是在減少DNS查詢和允許高度并行下載之間作出的最好權衡
    (高度并行下載,瀏覽器一次能并發加載的量是受域名控制的)
  • 2.使用Keep-alive進行持久連接

4.網絡的請求過程怎樣走最近的網絡

網站通常將其所有的服務器都放在同一個地方,當用戶群增加時,公司就必須在多個地理位置不同的服務器上部署內容
為了縮短http請求的時間,我們應該把大量的靜態資源放置的離用戶近一點。

內容發布網絡CDN(Content Delivery Networks)
CDN是一組分布在多個不同地理位置的web服務器,用于更加有效的向用戶發布內容

基本思路:

盡可能避開互聯網上有可能影響數據傳輸速度和穩定性的瓶頸和環節,使內容傳輸的更快、更穩定。
通過在網絡各處放置節點服務器所構成的在現有的互聯網基礎之上的一層智能虛擬網絡,
CDN系統能夠實時地根據網絡流量和各節點的連接、負載狀況以及到用戶的距離和響應時間等綜合信息
將用戶的請求重新導向離用戶最近的服務節點上。

基礎架構:最簡單的CDN網絡由一個DNS服務器和幾臺緩存服務器組成

  • 1.當用戶點擊網站頁面上的內容URL,經過本地DNS系統解析,
    DNS系統會最終將域名的解析權交給CNAME指向的CDN專用DNS服務器。
  • 2.CDN的DNS服務器將CDN的全局負載均衡設備IP地址返回用戶。
  • 3.用戶向CDN的全局負載均衡設備發起內容URL訪問請求。
  • 4.CDN全局負載均衡設備根據用戶IP地址,以及用戶請求的內容URL,
    選擇一臺用戶所屬區域的區域負載均衡設備,告訴用戶向這臺設備發起請求。
  • 5.區域負載均衡設備會為用戶選擇一臺合適的緩存服務器提供服務,
    • 選擇的依據包括:根據用戶IP地址,判斷哪一臺服務器距用戶最近;
    • 根據用戶所請求的URL中攜帶的內容名稱,判斷哪一臺服務器上有用戶所需內容;
    • 查詢各個服務器當前的負載情況,判斷哪一臺服務器尚有服務能力。
    • 基于以上這些條件的綜合分析之后,
    • 區域負載均衡設備會向全局負載均衡設備返回一臺緩存服務器的IP地址。
  • 6.全局負載均衡設備把服務器的IP地址返回給用戶。
  • 7.用戶向緩存服務器發起請求,緩存服務器響應用戶請求,將用戶所需內容傳送到用戶終端。
    如果這臺緩存服務器上并沒有用戶想要的內容,而區域均衡設備依然將它分配給了用戶,
    那么這臺服務器就要向它的上一級緩存服務器請求內容,直至追溯到網站的源服務器將內容拉到本地。

緩存機制

即便緩存服務器(客戶端瀏覽器)內有緩存文件,也不能保證每次都會使用到對應資源的緩存,這和被緩存資源的有效性有關
列如客戶端的要求,緩存的有效期等
當遇上源服務器上的資源更新時,如果還是使用不變的緩存,那就演變成返回更新前的舊資源了

1.客戶端自己決定的緩存策略

實體首部字段Expires(響應)

  • 首部字段Expires會將資源失效的日期告知客戶端,緩存服務器在接收到含有首部字段Expires的響應后,會以緩存來應答請求。
  • 在Expires字段值指定的時間之前,響應的副本會一直被保存。
  • 當超過指定的時間后緩存服務器在請求發送過來時,會轉向源服務器請求資源。
  • 源服務器不希望緩存服務器對資源緩存時,最好在Expires字段內寫入與首部字段Date相同的時間值。
  • 但是在首部字段cache-control有指定max-age指令時,比起首部字段Expires,會優先處理max-age指令

通用首部字段cache-control

響應指令 說明
max-age[秒] 響應的最大age值
no-cache 緩存前必須先確認其有效性
no-store 不緩存請求或響應的任何內容

首部字段cache-control有指定max-age指令時,比起首部字段Expires,會優先處理max-age指令

客戶端與服務端協商的緩存策略

實體首部字段last-modified(響應)& 請求首部字段if-modified-since

  • 首部last-modified指明資源最終修改的時間,一般來說,這個值就是資源被修改的時間。

  • 首部字段if-modified-since

    • 第一次請求資源時,資源在響應頭中種入last-modified字段,并隨著響應體一起存到緩存中
    • 下一次需要再發送請求時,請求體中會將上一次修改時間(last-modified)種入if-modified-since字段中帶到服務端,它會告知服務器:
      • 若在if-modified-since字段值之后對應的資源都沒有更新過則返回304 Not Modified狀態碼
      • 若在if-modified-since字段值之后 對應的資源有過更新 則希望服務器能處理成功 200
        失敗 500

此處的客戶端與服務端協商的緩存策略一般與cache-control一塊使用。需要在cache-control失效后再走這種緩存策略

缺點:

  • 1.某些服務端沒有辦法獲取精確的修改時間,導致last-modified有問題
  • 2.文件時間修改了,但文件內容卻沒有變

響應首部字段etag & 請求首部字段if-None-Match

  • 響應首部字段etag:

    • 它可以告知客戶端實體標識,它是一種可以將資源以字符串做唯一標識的方式,服務器會為每份資源分配對應的Etag值。
    • 另外當資源更新時,etag的值也需要更新,這個唯一標識的生成沒有規定統一的算法,由服務器自行決定
  • 請求首部字段if-None-Match

    • 機制和if-modified-since差不多,當if-None-Match字段與etag不一致時,就告知服務器該處理這請求

此處的客戶端與服務端協商的緩存策略一般與cache-control一塊使用。需要在cache-control失效后再走這種緩存策略

總結 分級緩存策略

  • 1.200 from cache

這一層由exprise(http1.0)、cache-control(http1.1)控制
cache-control的優先級要高于exprise
當它們沒有失效時,瀏覽器只能訪問總結的緩存

  • 2.304狀態

這一層由last-modified或etag來控制,
etag優先級比last-modified高
當上一層失效時,用戶刷新時瀏覽器會發請求給服務器,如果服務端沒有變化這返回304給瀏覽器

  • 3.200狀態

當上兩層都失效時,瀏覽器會去服務器下載最新的數據

原文地址 https://blog.fushily.cn/2018/03/20/%E5%89%8D%E7%AB%AF%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E8%B5%84%E6%96%99%E6%95%B4%E7%90%86/.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容