1. Chrome 打開一個頁面需要啟動多少進程?分別有哪些進程? 瀏覽器從關閉狀態進行啟動,然後新開 1 個頁面至少需要 1 個網路進程、1 個瀏覽器進程、1 個 GPU 進程以及 1 個渲染進程,共 4 個進程;後續再新開標簽頁,瀏覽器、網路進程、GPU進程是共用的,不會重新啟動,如果2個頁面屬 ...
1. Chrome 打開一個頁面需要啟動多少進程?分別有哪些進程?
瀏覽器從關閉狀態進行啟動,然後新開 1 個頁面至少需要 1 個網路進程、1 個瀏覽器進程、1 個 GPU 進程以及 1 個渲染進程,共 4 個進程;後續再新開標簽頁,瀏覽器、網路進程、GPU進程是共用的,不會重新啟動,如果2個頁面屬於同一站點的話,並且從a頁面中打開的b頁面,那麼他們也會共用一個渲染進程,否則新開一個渲染進程。
最新的 Chrome 瀏覽器包括:1 個瀏覽器(Browser)主進程、1 個 GPU 進程、1 個網路(NetWork)進程、多個渲染進程和多個插件進程。
瀏覽器進程
:主要負責界面顯示、用戶交互、子進程管理,同時提供存儲等功能。渲染進程
:核心任務是將 HTML、CSS 和 JavaScript 轉換為用戶可以與之交互的網頁,排版引擎 Blink 和 JavaScript 引擎 V8 都是運行在該進程中,預設情況下,Chrome 會為每個 Tab 標簽創建一個渲染進程。出於安全考慮,渲染進程都是運行在沙箱模式下。GPU 進程
:其實,Chrome 剛開始發佈的時候是沒有 GPU 進程的。而 GPU 的使用初衷是為了實現 3D CSS 的效果,只是隨後網頁、Chrome 的 UI 界面都選擇採用 GPU 來繪製,這使得 GPU 成為瀏覽器普遍的需求。最後,Chrome 在其多進程架構上也引入了 GPU 進程。網路進程
:主要負責頁面的網路資源載入,之前是作為一個模塊運行在瀏覽器進程裡面的,直至最近才獨立出來,成為一個單獨的進程。插件進程
:主要是負責插件的運行,因插件易崩潰,所以需要通過插件進程來隔離,以保證插件進程崩潰不會對瀏覽器和頁面造成影響。
2. 如何保證頁面文件能被完整送達瀏覽器?
互聯網中的數據是通過數據包來傳輸的。數據包要在互聯網上進行傳輸,就要符合網際協議(IP),互聯網上不同的線上設備都有唯一的地址,地址只是一個數字,只要知道這個具體的地址,就可以往這裡發送信息。
如果要想把一個數據包從主機 A 發送給主機 B,那麼在傳輸之前,數據包上會被附加上主機 B 的 IP 地址信息,這樣在傳輸過程中才能正確定址。額外地,數據包上還會附加上主機 A 本身的 IP 地址,有了這些信息主機 B 才可以回覆信息給主機 A。這些附加的信息會被裝進一個叫 IP 頭的數據結構里。IP 頭是 IP 數據包開頭的信息,包含 IP 版本、源 IP 地址、目標 IP 地址、生存時間等信息。
IP 是非常底層的協議,只負責把數據包傳送到對方電腦,但是對方電腦並不知道把數據包交給哪個程式,是交給瀏覽器還是交給王者榮耀?因此,需要基於 IP 之上開發能和應用打交道的協議,最常見的是用戶數據包協議(User Datagram Protocol),簡稱UDP
和傳輸控制協議(Transmission Control Protocol),簡稱TCP
.
基本傳輸過程為:
- 上層將數據包交給傳輸層
- 傳輸層會在數據包前面附加上
UDP 頭
,組成新的 UDP 數據包,再將新的 UDP 數據包交給網路層 - 網路層再將 IP 頭附加到數據包上,組成新的 IP 數據包,並交給底層
- 數據包被傳輸到主機 B 的網路層,在這裡主機 B 拆開 IP 頭信息,並將拆開來的數據部分交給傳輸層
- 在傳輸層,數據包中的 UDP 頭會被拆開,並根據 UDP 中所提供的埠號,把數據部分交給上層的應用程式
- 最終,數據包就發送到了主機 B 上層應用程式這裡。
3. UDP和TCP有什麼區別?
- TCP協議在傳送數據段的時候要給段標號;UDP協議不
- TCP協議可靠;UDP協議不可靠
- TCP協議是面向連接;UDP協議採用無連接
- TCP協議負載較高,採用虛電路;UDP採用無連接
- TCP協議的發送方要確認接收方是否收到數據段(3次握手協議)
- TCP協議採用視窗技術和流控制
特性 | TCP | UDP |
---|---|---|
是否連接 | 面向連接 | 面向非連接 |
傳輸可靠性 | 可靠 | 不可靠 |
應用場合 | 傳輸大量數據 | 傳輸少量數據 |
速度 | 慢 | 快 |
4. TCP傳輸的詳細過程是怎樣的?
進行三次握手,建立TCP連接。
- 第一次握手:建立連接。客戶端發送連接請求報文段,將SYN位置為1,Sequence Number為x;然後,客戶端進入SYN_SEND狀態,等待伺服器的確認;
- 第二次握手:伺服器收到SYN報文段。伺服器收到客戶端的SYN報文段,需要對這個SYN報文段進行確認,設置Acknowledgment Number為x+1(Sequence Number+1);同時,自己自己還要發送SYN請求信息,將SYN位置為1,Sequence Number為y;伺服器端將上述所有信息放到一個報文段(即SYN+ACK報文段)中,一併發送給客戶端,此時伺服器進入SYN_RECV狀態;
- 第三次握手:客戶端收到伺服器的SYN+ACK報文段。然後將Acknowledgment Number設置為y+1,向伺服器發送ACK報文段,這個報文段發送完畢以後,客戶端和伺服器端都進入ESTABLISHED狀態,完成TCP三次握手。
完成了三次握手,客戶端和伺服器端就可以開始傳送數據。
ACK:此標誌表示應答域有效,就是說前面所說的TCP應答號將會包含在TCP數據包中;有兩個取值:0和1,為1的時候表示應答域有效,反之為0。
TCP協議規定,只有ACK=1時有效,也規定連接建立後所有發送的報文的ACK必須為1。
SYN(SYNchronization) : 在連接建立時用來同步序號。當SYN=1而ACK=0時,表明這是一個連接請求報文。對方若同意建立連接,則應在響應報文中使SYN=1和ACK=1. 因此, SYN置1就表示這是一個連接請求或連接接受報文。
FIN (finis)即完,終結的意思, 用來釋放一個連接。當 FIN = 1 時,表明此報文段的發送方的數據已經發送完畢,並要求釋放連接。
發送HTTP請求,伺服器處理請求,返迴響應結果
TCP連接建立後,瀏覽器就可以利用HTTP/HTTPS協議向伺服器發送請求了。伺服器接受到請求,就解析請求頭,如果頭部有緩存相關信息如if-none-match與if-modified-since,則驗證緩存是否有效,若有效則返回狀態碼為304,若無效則重新返回資源,狀態碼為200.
關閉TCP連接
- 第一次分手:主機1(可以使客戶端,也可以是伺服器端),設置Sequence Number和Acknowledgment Number,向主機2發送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有數據要發送給主機2了;
- 第二次分手:主機2收到了主機1發送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number為Sequence Number加1;主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我“同意”你的關閉請求;
- 第三次分手:主機2向主機1發送FIN報文段,請求關閉連接,同時主機2進入LAST_ACK狀態;
- 第四次分手:主機1收到主機2發送的FIN報文段,向主機2發送ACK報文段,然後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段以後,就關閉連接;此時,主機1等待2MSL後依然沒有收到回覆,則證明Server端已正常關閉,那好,主機1也可以關閉連接了。
5. 為什麼很多站點第二次打開速度會很快?
主要原因是第一次載入頁面過程中,緩存了一些耗時的數據。
那麼,哪些數據會被緩存呢?
DNS緩存
主要就是在瀏覽器本地把對應的 IP 和功能變數名稱關聯起來,這樣在進行DNS解析的時候就很快。
MemoryCache
是指存在記憶體中的緩存。從優先順序上來說,它是瀏覽器最先嘗試去命中的一種緩存。從效率上來說,它是響應速度最快的一種緩存。
記憶體緩存是快的,也是“短命”的。它和渲染進程“生死相依”,當進程結束後,也就是 tab 關閉以後,記憶體里的數據也將不復存在。
瀏覽器緩存
先看一張經典的流程圖,結合理解
瀏覽器緩存,也稱Http緩存,分為強緩存和協商緩存。優先順序較高的是強緩存,在命中強緩存失敗的情況下,才會走協商緩存。
強緩存
強緩存
是利用 http 頭中的 Expires
和 Cache-Control
兩個欄位來控制的。強緩存中,當請求再次發出時,瀏覽器會根據其中的 expires 和 cache-control 判斷目標資源是否“命中”強緩存,若命中則直接從緩存中獲取資源,不會再與服務端發生通信。
實現強緩存,過去我們一直用expires。當伺服器返迴響應時,在 Response Headers 中將過期時間寫入 expires 欄位。像這樣
expires: Wed, 12 Sep 2019 06:12:18 GMT
可以看到,expires 是一個時間戳,接下來如果我們試圖再次向伺服器請求資源,瀏覽器就會先對比本地時間和 expires 的時間戳,如果本地時間小於 expires 設定的過期時間,那麼就直接去緩存中取這個資源。
從這樣的描述中大家也不難猜測,expires 是有問題的,它最大的問題在於對“本地時間”的依賴。如果服務端和客戶端的時間設置可能不同,或者我直接手動去把客戶端的時間改掉,那麼 expires 將無法達到我們的預期。
考慮到 expires 的局限性,HTTP1.1 新增了Cache-Control
欄位來完成 expires 的任務。expires 能做的事情,Cache-Control 都能做;expires 完成不了的事情,Cache-Control 也能做。因此,Cache-Control 可以視作是 expires 的完全替代方案。在當下的前端實踐里,我們繼續使用 expires 的唯一目的就是向下相容。
cache-control: max-age=31536000
在 Cache-Control 中,我們通過max-age來控制資源的有效期。max-age 不是一個時間戳,而是一個時間長度。在本例中,max-age 是 31536000 秒,它意味著該資源在 31536000 秒以內都是有效的,完美地規避了時間戳帶來的潛在問題。
Cache-Control 相對於 expires 更加準確,它的優先順序也更高。當 Cache-Control 與 expires 同時出現時,我們以 Cache-Control 為準。
協商緩存
協商緩存依賴於服務端與瀏覽器之間的通信。協商緩存機制下,瀏覽器需要向伺服器去詢問緩存的相關信息,進而判斷是重新發起請求、下載完整的響應,還是從本地獲取緩存的資源。如果服務端提示緩存資源未改動(Not Modified),資源會被重定向到瀏覽器緩存,這種情況下網路請求對應的狀態碼是 304。
協商緩存的實現,從 Last-Modified
到 Etag
,Last-Modified 是一個時間戳,如果我們啟用了協商緩存,它會在首次請求時隨著 Response Headers 返回:
Last-Modified: Fri, 27 Oct 2017 06:35:57 GMT
隨後我們每次請求時,會帶上一個叫 If-Modified-Since 的時間戳欄位,它的值正是上一次 response 返回給它的 last-modified 值:
If-Modified-Since: Fri, 27 Oct 2017 06:35:57 GMT
伺服器接收到這個時間戳後,會比對該時間戳和資源在伺服器上的最後修改時間是否一致,從而判斷資源是否發生了變化。如果發生了變化,就會返回一個完整的響應內容,併在 Response Headers 中添加新的 Last-Modified 值;否則,返回如上圖的 304 響應,Response Headers 不會再添加 Last-Modified 欄位。
使用 Last-Modified 存在一些弊端,這其中最常見的就是這樣兩個場景:
- 我們編輯了文件,但文件的內容沒有改變。服務端並不清楚我們是否真正改變了文件,它仍然通過最後編輯時間進行判斷。因此這個資源在再次被請求時,會被當做新資源,進而引發一次完整的響應——不該重新請求的時候,也會重新請求。
- 當我們修改文件的速度過快時(比如花了 100ms 完成了改動),由於 If-Modified-Since 只能檢查到以秒為最小計量單位的時間差,所以它是感知不到這個改動的——該重新請求的時候,反而沒有重新請求了。
這兩個場景其實指向了同一個 bug——伺服器並沒有正確感知文件的變化。為瞭解決這樣的問題,Etag 作為 Last-Modified 的補充出現了。
Etag
是由伺服器為每個資源生成的唯一的標識字元串,這個標識字元串可以是基於文件內容編碼的,只要文件內容不同,它們對應的 Etag 就是不同的,反之亦然。因此 Etag 能夠精準地感知文件的變化。
Etag 的生成過程需要伺服器額外付出開銷,會影響服務端的性能,這是它的弊端。因此啟用 Etag 需要我們審時度勢。正如我們剛剛所提到的——Etag 並不能替代 Last-Modified,它只能作為 Last-Modified 的補充和強化存在。
Etag 在感知文件變化上比 Last-Modified 更加準確,優先順序也更高。當 Etag 和 Last-Modified 同時存在時,以 Etag 為準。
Service Worker Cache
Service Worker 是一種獨立於主線程之外的 Javascript 線程。它脫離於瀏覽器窗體,因此無法直接訪問 DOM。這樣獨立的個性使得 Service Worker 的“個人行為”無法干擾頁面的性能,這個“幕後工作者”可以幫我們實現離線緩存、消息推送和網路代理等功能。我們藉助 Service worker 實現的離線緩存就稱為 Service Worker Cache。
Service Worker 的生命周期包括 install、active、working 三個階段。一旦 Service Worker 被 install,它將始終存在,只會在 active 與 working 之間切換,除非我們主動終止它。這是它可以用來實現離線存儲的重要先決條件.
Push Cache
Push Cache 是指 HTTP2 在 server push 階段存在的緩存。這塊的知識比較新,應用也還處於萌芽階段,應用範圍有限不代表不重要——HTTP2 是趨勢、是未來。在它還未被推而廣之的此時此刻,我仍希望大家能對 Push Cache 的關鍵特性有所瞭解:
- Push Cache 是緩存的最後一道防線。瀏覽器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情況下才會去詢問 Push Cache。
- Push Cache 是一種存在於會話階段的緩存,當 session 終止時,緩存也隨之釋放。
- 不同的頁面只要共用了同一個 HTTP2 連接,那麼它們就可以共用同一個 Push Cache。
參考資料
- 極客時間《瀏覽器工作原理與實踐》
- 掘金小冊子《前端性能優化原理與實踐》
最後
- 歡迎加我微信(winty230),拉你進技術群,長期交流學習...
- 歡迎關註「前端Q」,認真學前端,做個有態度的技術人...