天天看點

一個頁面從輸入URL到加載顯示完成,發生了什麼?

面試經典題——URL加載

一、涉及基本知識點:

1. 計算機網絡

  1. 五層因特爾協定棧:
  • 應用層(dns、http):DNS解析成IP并完成http請求發送;
  • 傳輸層(tcp、udp):三次握手四次揮手模式建立tcp連接配接;
  • 網絡層(IP、ARP):IP尋址;
  • 資料鍊路層(PPP):将請求資料封裝成幀;
  • 實體層:利用實體媒體傳輸比特流(傳輸的時候通過雙絞線、電磁波等)
  • OIS七層架構:多了兩層即,會話層(處理兩個通信系統中交換資訊的表示方式)和表示層(管理不同使用者和程序之間的對話)。
  1. get和post的差別:
  • get産生一個tcp資料包,post産生兩個
  • get請求時會把headers和data資料一起發送出去;
  • post請求時,浏覽器先發送headers,伺服器100繼續,浏覽器再發送data。
  1. DNS查詢得到IP
  1. 請求資訊:首先檢視域名的本地DNS緩存,該緩存存儲計算機最近檢索到的資訊,如果計算機不知道答案,那麼就需要執行一個DNS查詢來查找答案;
  2. 詢問遞歸式DNS伺服器:
  • 如果資訊不存儲在本地,計算機會聯系您的ISP(網絡提供商)的遞歸DNS伺服器;
  • 這些專用計算機會為你執行一個DNS查詢工作;
  • 遞歸伺服器有自己的緩存,是以這個查詢過程通常在這裡完成,并将資訊還回給使用者;
  1. 詢問根域名伺服器
  • 如果遞歸伺服器沒有答案,他們會查詢根域名伺服器;
  • 根域名伺服器是一種計算機,它扮演着一種DNS的電話接線員的角色,他們不知道答案,但可以将我們的疑問指向知道在哪裡可以找到答案的人。
  1. 詢問TLD域名伺服器:
  • 根域名伺服器将檢視請求的第一部分,按從右到左的順序,從www.dyn.com中找到.com,并将請求指向.com對應的頂級域名伺服器(TLD).com;
  • 每個TLD,如(.com,.org,.us)都有自己的頂級域名伺服器,
  • 這些伺服器沒有我們需要的資訊,但他們可以直接将我們引導到有資訊的伺服器。
  1. 詢問權威的DNS伺服器
  • TLD域名伺服器會繼續檢查請求的下一部分(dyn)www.dyn.com,并将查詢指向負責此特定域名的伺服器;
  • 這些權威的伺服器将負責了解關于特定域的所有資訊,并将資訊存儲在DNS記錄。
  1. 找回記錄: -遞歸伺服器從權威伺服器中檢索dyn.com的記錄,并将記錄存儲在本地緩存;
  • 如果其他任何人請求dyn.com的主機記錄,遞歸伺服器已經有答案了,并不需要再次進行查找;
  • 所有記錄都有一個期限,一段時間後,遞歸伺服器将需要要求一個新的記錄副本,以確定資訊不回過時。
  1. 接收答案:
  • 有了答案,遞歸伺服器将記錄傳回到計算機,
  • 您的計算機将記錄存儲在緩存中,從記錄中讀取IP位址,然後将這些資訊傳遞給浏覽器;
  • 然後浏覽器就可以根據IP位址和伺服器進行連接配接建立。
  1. TCPIP請求
  • http的本質就是TCPIP請求;
  • 需要經曆3次握手建立連接配接,4次揮手斷開連接配接;
  • TCP将http長封包劃分為短封包,通過三次握手與伺服器端建立連接配接,進行可靠傳輸。
  • 三次握手:
  • 用戶端:你是XXX服務端嗎?
  • 服務端: 我是XXX服務端,你是用戶端嗎?
  • 客服端: 是的,我是用戶端
  • 建立連接配接成功後,接下來就可以進行正式的傳輸資料。
  • 四次揮手斷開連接配接
  • 主動方:我已經關閉了向你那邊的資訊發送通道,隻能被動接受資訊了;
  • 被動方: 收到通道關閉的資訊;
  • 被動方: 我現在也關閉了向你那邊發送資訊的通道
  • 主動方: 左後收到資訊,連接配接斷開,之後雙方無法通信
  1. TCP/IP的并發限制:
  • 浏覽器對同一個域名下并發的TCP連接配接是有限制的(2-10個不等)
  • 而且在http1.0中往往一個資源的下載下傳就需要一個tcp/ip請求

2. 浏覽器機制

(1)程序和線程的概念

  1. 程序是CPU資源配置設定的最小機關,是能擁有資源和獨立運作的最小機關;
  2. 線程是CPU排程的最小機關,線程是建立在程序的基礎上的一次程式運作機關,一個程序可以擁有多個線程;
  3. 通俗的講:程序是一個工廠,工廠有它獨立的資源,工廠之間互相獨立->程序之間互相獨立,線程是工廠中的勞工,多個勞工之間可以協作完成任務,工廠内有一個或多個勞工,勞工之間共享空間。

(2)多程序的浏覽器

浏覽器是多程序的,有一個主要程序,以及每一個tab頁面都會開一個程序(某些情況下多個tab由于優化政策會合并)
  • 浏覽器主要程序:
  1. Browser程序:浏覽器的主程序,負責協調、主要,隻有一個,作用:
  • 負責浏覽器界面的顯示、與使用者互動(如前進、後退等)
  • 負責各個頁面的管理,建立和銷毀其他程序;
  • 将Renderer程序得到的記憶體中的Bitmap繪制到使用者界面上
  • 網絡資源的管理和下載下傳等
  1. 第三方插件程序: 每種類型的插件對應一個程序,僅當該插件使用時才建立;
  2. GPU程序: 最多一個,用于3D繪制等;
  3. 浏覽器渲染程序(Renderer程序、浏覽器核心、内部是多線程):
  • 預設沒打開一個tab頁面,就會啟動一個Renderer程序;
  • 負責頁面的渲染,腳本的執行,事件的處理。
  • 浏覽器多程序的優勢
  1. 避免單個page crash影響整個浏覽器;
  2. 避免第三方插件crash影響整個浏覽器
  3. 多程序充分利用多核優勢;
  4. 友善使用沙盒模型隔離插件等程序,提高浏覽器穩定性
簡單點了解:如果浏覽器是單程序,那麼某個tab頁或第三方插件崩潰了,就會導緻整個浏覽器崩潰,體驗度極差,不過多程序記憶體消耗會更大,有點用空間換時間。

浏覽器核心(渲染程序)

  • 浏覽器渲染程序内部是多線程,包含主要線程有:

1.GUI渲染線程:

  • (1)負責浏覽器界面的渲染,解析HTML、CSS,建構DOM樹和RenderObject樹,布局和繪制等;
  • (2) 當界面需要重繪(Repaint)或由于某種操作引發回流(reflow)時該線程會執行;
  • 注意:GUI渲染線程和JS引擎線程是互斥的,當JS引擎執行時GUI線程會被挂起,GUI更新會儲存在一個隊列中等JS引擎空閑時立即執行。

2.JS引擎線程:

  • JS核心,負責處理JavaScript腳本程式(V8引擎)
  • 負責解析JavaScript腳本,運作代碼;
  • JS引擎一直等待着任務隊列中的任務到來,然後加以處理,一個tab頁面(renderer程序)中無論什麼時候都隻有一個JS線程在運作JS程式;
  • 注意:由于GUI渲染線程和JS引擎線程是互斥的,是以如果JS程式運作時間過長,這樣會導緻頁面渲染不連貫,導緻頁面渲染加載阻塞;

3.事件觸發線程:

  • 歸屬于浏覽器,而不是JS引擎,用來控制事件循環;
  • 當JS引擎執行代碼塊如setTimeOut時(也可以來自浏覽器核心的其他線程,如滑鼠單擊事件、AJAX異步請求等),會将對應的任務添加到事件線程中;
  • 當對應的事件符合觸發條件被觸發時,該線程就會把事件添加到JS的待處理隊列的隊尾,等待JS引擎的處理;
  • 注意:由于JS的單線程的關系是以這些待處理隊列中的事件都得排隊等待JS引擎處理(當JS引擎空閑時才會去執行)。

4.定時觸發器線程:

  • setTimeOut與setInterval所在的線程;
  • 浏覽器的定時計數器并不是由JavaScript引擎計數的,(因為JavaScript是單線程,如果處于阻塞狀态就會影響計時的準确)是以通過單獨的線程來計時并觸發定時(計時完畢後,添加到事件隊列,等待JS引擎空閑時執行)

5.異步http請求線程:

  • 在XMLHttpRequest在連接配接後是通過浏覽器新開一個線程請求的
  • 将檢測到狀态變更時,如果設定有回調函數,異步線程就将産生狀态變更事件,将這個回調在放到事件隊列中,再由JavaScript引擎執行。

一、 一個頁面從輸入URL到加載顯示完成,這個過程發生了什麼?

  • 簡潔版:
  • 浏覽器根據請求的URL交給DNS域名解析,找到真實的IP,向伺服器發起請求;
  • 伺服器交給背景處理完成後傳回資料,浏覽器接收檔案(HTML、CSS、JavaScript等);
  • 浏覽器對加載到的資源(HTML、CSS、JavaScript等)進行文法解析,建構相應的内部資料結構(DOM樹、CSS樹、render樹等);
  • 載入解析到的資源檔案、渲染頁面、完成。
  • 詳細版:
  1. 首先浏覽器開啟一個線程來處理這個請求,對URL分析判斷,如果是http協定就按照Web方式來處理;
  2. 其次浏覽器會對URL進行解析,一般包括(協定頭、主機域名或IP位址、端口号、請求路徑、查詢參數、hash等),然後開啟網絡線程發出一個完整到http請求;
  3. 當然一般我們輸入的URL是伺服器域名,這時就需要DNS通過域名查詢得到對應的IP;
  4. DNS首先會檢視浏覽器DNS緩存,沒有就查詢計算機本地DNS緩存,還沒有就詢問遞歸式DNS伺服器(即網絡提供商,一般這個伺服器都會有自己的緩存,是以IP查詢一般在這裡完成),如果沒有緩存,那就需要通過根域名和TLD域名伺服器指到對應的權威DNS伺服器找回記錄,并緩存到遞歸式伺服器,然後遞歸伺服器在将記錄傳回給本地。
  5. 有了IP位址,此時網絡層便會通過IP位址尋的對應伺服器的實體位址
  6. 尋得伺服器位址,用戶端在網絡傳輸層便可以和伺服器通過三次握手建立tcpip連接配接
  7. 連接配接建立後網絡資料鍊路層将資料包裝成幀;
  8. 最後實體層利用實體媒體進行傳輸;
  9. 到了伺服器,就會通過相反的方式将資料一層一層的還原回去;
  10. 請求到了背景伺服器,一般會有統一的驗證,如安全驗證、跨域驗證等,驗證未通過就直接傳回相應的http封包
  11. 驗證通過後,就會進入背景代碼,此時程式收到請求,然後執行對應的操作(如查詢資料庫等);
  12. 如果浏覽器通路過,且緩存上有對應的資源,便會與伺服器最後修改時間對比,一緻便傳回304,告訴浏覽器可使用本地緩存;
  13. 前端浏覽器接收到響應成功的封包後便開始下載下傳網頁
  14. 下載下傳完的網頁将被交給浏覽器核心(渲染程序)進行處理:
  1. 根據頂部定義的DTD類型進行對應的解析方式;
  2. 渲染程序内部是多線程的,網頁的解析将會被交給内部的GUI渲染線程處理;
  3. 首先渲染線程中的HTML解釋器,将HTML網頁和資源從位元組流解釋轉換成字元流;
  4. 再通過詞法分析器将字元流解釋成詞語;
  5. 之後經過文法分析器根據詞語建構成節點;最後通過這些節點組建一個DOM樹;
  6. 這個過程中,如果遇到的DOM節點是JavaScript代碼,就會調用JavaScript引擎對JavaScript代碼進行解釋執行,此時由JavaScript引擎和GUI渲染線程的互斥,GUI渲染線程就會被挂起,渲染過程停止;如果JavaScript代碼的運作中對DOM樹進行了修改,那麼DOM的建構需要從新開始;
  7. 如果節點需要依賴其他資源,如(圖檔,CSS等),便會調用網絡子產品的資源加載器來加載它們,但它們是異步的,不會阻塞目前DOM樹的建構;
  8. 如果遇到的是JavaScript資源URL(沒有标記異步),則需要停止目前DOM的建構,直到JavaScript的資源加載并被JavaScript引擎執行後才繼續建構DOM;
  9. 對于CSS,CSS解釋器會将CSS檔案解釋成内部表示結構,生成CSS規則樹;
  10. 然後合并CSS規則樹和DOM樹,生成render渲染樹;
  11. 最後對render樹進行布局和繪制,并将結果通過IO線程傳遞給Browser控制程序進行顯示。

繼續閱讀