最近在準備前端的面試,這個問題基本是必考題,稍微去網上查閱了一些資料,並且自己整理了下。
總的過程如下:
- 輸入 URL
- DNS 解析
- 建立 TCP 連結
- 發送 HTTP 請求
- 伺服器永久重定向
- 伺服器處理請求並返回一個 HTTP 響應
- 瀏覽器顯示 HTML
- 連結結束
輸入 URL#
URL 的中文名叫統一資源定位符,用於得到資源的位置和訪問方法。
其組成為:協議:// 主機名:端口號 / 路徑 /; 參數?查詢 #信息片段
DNS 解析#
DNS(域名系統),因特網上作為域名和 IP 地址相互映射的一個分佈式數據庫,得到主機名對應的 IP 地址的過程就叫做域名解析。
DNS 解析的過程其實就是為了尋找哪台機器上有需要的資源,實際上充當了一個翻譯的身份,將輸入的網址轉換成 IP 地址。
以下是 DNS 的一個查找順序:
- 瀏覽器緩存:向瀏覽器緩存中讀取訪問記錄
- 操作系統緩存:查找在系統運行內存中的緩存
- host 文件:查找本地硬盤的 host 文件
- 路由器緩存:部分路由器會緩存訪問過的域名
- ISP(互聯網服務提供商)DNS 緩存:在本地查找不到的情況下,ISP 會在當前伺服器的緩存中查找
- 根 DNS 伺服器:根域名收到請求,判斷是哪台伺服器管理,並返回頂級 DNS 伺服器的 IP 給請求者。
在查找完以後本地 DNS 伺服器向域名的解析伺服器發起請求,本地伺服器將 IP 地址返回給電腦,並將對應關係保存在緩存中。
拓展:
DNS 的查詢方式:
- 遞歸:局部 DNS 伺服器負責向其他 DNS 伺服器查詢(一般先向該域名的根域伺服器查詢,接著一級一級向下查詢),結果返回給局部 DNS 伺服器後再由其返回個客戶端。
- 迭代:局部 DNS 伺服器把能解析該域名的其他 DNS 伺服器的 IP 地址給客戶端 DNS 程式,再由該程式向這些 DNS 伺服器查詢(用於局部 DNS 伺服器不能回答客戶機 DNS 查詢時)。
DNS 優化方法:
- DNS 緩存
- DNS 負載均衡
- 為啥需要:當每次請求的資源都在同一台機器上時,機器可能承受不過來而崩掉。
- 原理:為一個主機名配置多個 IP 地址,在應答查詢餓時候對每個查詢以 DNS 文件中記錄的 IP 地址按順序返回不同的結果,將訪問引導到不同的機器上去。
建立 TCP 連結#
拿到 IP 地址後就是通過三次握手來建立 TCP 連結了。
- 第一次握手:客戶端發送SYN(同步序列編號)包到伺服器,並且進入SYN_SENT狀態,等待伺服器確認。
- 第二次握手:伺服器收到 SYN 包後確認,同時自己也發送一個 SYN 包,即SYN+ACK 包,伺服器進入SYN_RECE狀態
- 第三次握手:客戶端收到伺服器的 SYN+ACK 包,向伺服器發送一個確認包 ACK,發送完畢後客戶端和伺服器進入ESTABLISHED狀態。
拓展:
為啥三次握手:為了防止已經失效的連結請求報文突然傳送到了伺服器因而產生錯誤。
發送 HTTP 請求#
建立 TCP 連結後客戶端發起 HTTP 請求,HTTP 報文包含三個部分:
- 請求行:請求方法 + URL + 協議 / 版本
- 請求報頭:傳遞請求的附加信息和客戶端自身的信息
- 請求正文:需要傳遞的數據
伺服器永久重定向#
伺服器給瀏覽器響應一個 301 永久重定向響應,例如訪問http://google.com/ 會自動跳轉到 http://www.google.com/
目的:
- 這樣就會把訪問帶 www 的和不帶 www 的地址歸到同一個網站排名下,網站在搜索鏈接的排名下就不會降低。
- 用不同的地址會導致緩存的良好性變差,一個頁面有多個名字的時候可能會在緩存中出現多次。
伺服器處理請求並返回 HTTP 報文#
後端從固定的端口接收到 TCP 報文後,會對 TCP 進行處理,對 HTTP 協議進行解析,按照報文格式進一步封裝成 HTTP Request 對象,供上層使用。
HTTP 響應由 4 個部分組成:
- 狀態行:協議版本、狀態代碼、狀態描述
- 響應頭:由鍵值對組成,每行一對,用 “:” 分割
- 空行: 分割請求數據
- 響應正文
拓展:
在大一點的網站中會將請求到反向代理中,將同一應用部署到多台伺服器上,將大量用戶請求分配給多台機器處理。
即客戶端先請求到 Nginx,Nginx 再請求應用伺服器,最後將結果返回客戶端。
瀏覽器顯示 HTML#
瀏覽器顯示 HTMl 是一個邊解析邊渲染的過程,大致的過程為:
- 解析 HTML 文件構建 DOM 樹
- 解析 CSS 文件構建渲染樹
- 瀏覽器開始布局渲染樹並將其繪製到屏幕上
拓展:
關於 reflow (回流) 和 repaint (重繪):
- DOM 節點中的各個元素都是以盒模型的形式存在,瀏覽器計算其位置、大小等屬性的這個過程稱之為 reflow。
- 當這些屬性都確定下來後,瀏覽器開始繪制內容,這個繪制的過程稱之為 repaint。
reflow 和 repaint 在頁面首次加載的時候是肯定要經歷的,但是這兩個過程都是十分消耗性能,應該盡可能減少。
js 解析以及執行機制:
當解析過程中遇到 JS 文件時,HTML 文檔會掛起渲染的線程,然後等待 js 文件加載並且解析完畢(由於 js 有可能會修改 DOM,例如 document.write),故平時 js 代碼也是放在 html 的末尾。
js 解析是由瀏覽器中的 js 解析引擎完成,js 是單線程運行,但是像 IO 讀寫等任務比較耗時,所以需要一種機制可以先執行排在後面的任務,即同步任務和異步任務。
js 的執行機制可以看做成一個主線程 + 一個任務隊列。
同步任務是放在主線程上的任務,在主線程上形成一個棧;
異步任務是放在任務隊列中的任務,有了運行結果就會在任務隊列中放置一個事件;
腳本先運行棧,然後從任務隊列中提取事件,運行裡面的任務。
這個過程不斷循環,也被稱為事件循環。
連結結束#
現在頁面為了優化請求耗時,一般都會持續著 TCP 的連結,而 TCP 連結斷開的時機是當前頁面關閉的時候。
接下來就是四次揮手斷開 TCP 連結:
- 主機發送一個 FIN,主機進入FIN_WAIT_1狀態。
- 服務端收到 FIN 後,發送一個 ACK 給主機,確認序號為收到序號 + 1,服務端進入CLOSE_WAIT狀態。
- 服務端發送一個 FIN 報文,用來關閉數據傳送,並且進入LAST_ACK狀態。
- 主機收到 FIN 後,進入 TIME_WAIT 狀態,接著發送一個 ACK 給服務端,確保服務端收到自己的 ACK 報文後進入CLOSED狀態