在瀏覽器中從URL輸入到頁面展示到底發生什麼?

前言

打開瀏覽器從輸入網址到網頁呈如今你們面前,背後到底發生了什麼?經歷怎麼樣的一個過程?先給你們來張整體流程圖,具體步驟請看下文分解!寫文章不易,請多多支持與關注!css

整體來講分爲如下幾個過程:html

  • DNS 解析:將域名解析成 IP 地址
  • TCP 鏈接:TCP 三次握手
  • 發送 HTTP 請求
  • 服務器處理請求並返回 HTTP 報文
  • 瀏覽器解析渲染頁面
  • 斷開鏈接:TCP 四次揮手

具體過程

URL 究竟是啥

URL(Uniform Resource Locator),統一資源定位符,用於定位互聯網上資源,俗稱網址。 好比 http://www.w3school.com.cn/html/index.asp,遵照如下的語法規則:前端

scheme://host.domain:port/path/filename 各部分解釋以下:nginx

  • scheme:定義因特網服務的類型。常見的協議有 http、https、ftp、file,其中最多見的類型是 http,而 https 則是進行加密的網絡傳輸。
  • host:定義域主機(http 的默認主機是 www)
  • domain:定義因特網域名,好比 w3school.com.cn
  • port:定義主機上的端口號(http 的默認端口號是 80)
  • path:定義服務器上的路徑(若是省略,則文檔必須位於網站的根目錄中)
  • filename: 定義文檔/資源的名稱

域名解析(DNS)

在瀏覽器輸入網址後,首先要通過域名解析,由於瀏覽器並不能直接經過域名找到對應的服務器,而是要經過 IP 地址。你們這裏或許會有個疑問----計算機既能夠被賦予 IP 地址,也能夠被賦予主機名和域名。好比 www.hackr.jp。那怎麼不一開始就賦予個 IP 地址?這樣就能夠省去解析麻煩。咱們先來了解下什麼是 IP 地址web

IP 地址

IP 地址是指互聯網協議地址,是 IP Address 的縮寫。IP 地址是 IP 協議提供的一種統一的地址格式,它爲互聯網上的每個網絡和每一臺主機分配一個邏輯地址,以此來屏蔽物理地址的差別。IP 地址是一個 32 位的二進制數,好比 127.0.0.1 爲本機 IP。 域名就至關於 IP 地址喬裝打扮的假裝者,帶着一副面具。它的做用就是便於記憶和溝通的一組服務器的地址。 用戶一般使用主機名或域名來訪問對方的計算機,而不是直接經過 IP 地址訪問。由於與 IP 地址的一組純數字相比,用字母配合數字的表示形式來指定計算機名更符合人類的記憶習慣。但要讓計算機去理解名稱,相對而言就變得困難了。由於計算機更擅長處理一長串數字。爲了解決上述的問題,DNS 服務應運而生。redis

什麼是域名解析

DNS 協議提供經過域名查找 IP 地址,或逆向從 IP 地址反查域名的服務。DNS 是一個網絡服務器,咱們的域名解析簡單來講就是在 DNS 上記錄一條信息記錄chrome

例如 baidu.com  220.114.23.56(服務器外網IP地址)80(服務器端口號)
複製代碼

域名解析過程

上述圖片是查找www.google.com的IP地址過程。apache

首先在本地域名服務器中查詢IP地址,若是沒有找到的狀況下,本地域名服務器會向根域名服務器發送一個請求,若是根域名服務器也不存在該域名時,本地域名會向com頂級域名服務器發送一個請求,依次類推下去。直到最後本地域名服務器獲得google的IP地址並把它緩存到本地,供下次查詢使用。從上述過程當中,能夠看出網址的解析是一個從右向左的過程: com -> google.com -> www.google.com。可是你是否發現少了點什麼,根域名服務器的解析過程呢?事實上,真正的網址是www.google.com.,並非我多打了一個.,這個.對應的就是根域名服務器,默認狀況下全部的網址的最後一位都是.,既然是默認狀況下,爲了方便用戶,一般都會省略,瀏覽器在請求DNS的時候會自動加上,全部網址真正的解析過程爲: . -> .com -> google.com. -> www.google.com.編程

DNS優化

瞭解了DNS的過程,能夠爲咱們帶來哪些?上文中請求到google的IP地址時,經歷了8個步驟,這個過程當中存在多個請求(同時存在UDP和TCP請求,爲何有兩種請求方式,請自行查找)。若是每次都通過這麼多步驟,是否太耗時間?如何減小該過程的步驟呢?那就是DNS緩存。json

DNS緩存

DNS存在着多級緩存,從離瀏覽器的距離排序的話,有如下幾種: 瀏覽器緩存操做系統緩存路由器緩存ISP服務器緩存根域名服務器緩存頂級域名服務器緩存主域名服務器緩存

  • 瀏覽器緩存:瀏覽器會按照必定的頻率緩存 DNS 記錄。

  • 操做系統緩存:若是瀏覽器緩存中找不到須要的 DNS 記錄,那就去操做系統中找。

  • 路由器緩存:路由器也有 DNS 緩存。

  • ISP服務器緩存:ISP 是互聯網服務提供商(Internet Service Provider)的簡稱,ISP 有專門的 DNS 服務器應對 DNS 查詢請求。

  • 根域名服務器緩存:ISP 的 DNS 服務器還找不到的話,它就會向根域名服務器發出請求,進行遞歸查詢(DNS 服務器先問根域名服務器.com 域名服務器的 IP 地址,而後再問.baidu 域名服務器,依次類推)

    • 在你的chrome瀏覽器中輸入chrome://net-internals/#dns,你能夠看到chrome瀏覽器的DNS緩存。
    • 操做系統緩存主要存在/etc/hosts(Linux系統)中
DNS負載均衡

不知道你們有沒有思考過一個問題:

DNS返回的IP地址是否每次都同樣?若是每次都同樣是否說明你請求的資源都位於同一臺機器上面,那麼這臺機器須要多高的性能和儲存才能知足億萬請求呢?其實真實的互聯網世界背後存在成千上百臺服務器,大型的網站甚至更多。可是在用戶的眼中,它須要的只是處理他的請求,哪臺機器處理請求並不重要。DNS能夠返回一個合適的機器的IP給用戶,例如能夠根據每臺機器的負載量該機器離用戶地理位置的距離等等,這種過程就是DNS負載均衡,又叫作DNS重定向。你們耳熟能詳的CDN(Content Delivery Network)就是利用DNS的重定向技術,DNS服務器會返回一個跟用戶最接近的點的IP地址給用戶,CDN節點的服務器負責響應用戶的請求,提供所需的內容。

小結

瀏覽器經過向 DNS 服務器發送域名,DNS 服務器查詢到與域名相對應的 IP 地址,而後返回給瀏覽器,瀏覽器再將 IP 地址打在協議上,同時請求參數也會在協議搭載,而後一併發送給對應的服務器。接下來介紹向服務器發送 HTTP 請求階段,HTTP 請求分爲三個部分:TCP 三次握手、http 請求響應信息、關閉 TCP 鏈接。

TCP 三次握手

在客戶端發送數據以前會發起 TCP 三次握手用以同步客戶端和服務端的序列號和確認號,並交換 TCP 窗口大小信息。

TCP 三次握手的過程以下:

  • 客戶端發送一個帶 SYN=1,Seq=X 的數據包到服務器端口(第一次握手,由瀏覽器發起,告訴服務器我要發送請求了)
  • 服務器發回一個帶 SYN=1, ACK=X+1, Seq=Y 的響應包以示傳達確認信息(第二次握手,由服務器發起,告訴瀏覽器我準備接受了,你趕忙發送吧)
  • 客戶端再回傳一個帶 ACK=Y+1, Seq=Z 的數據包,表明「握手結束」(第三次握手,由瀏覽器發送,告訴服務器,我立刻就發了,準備接受吧)

爲啥須要三次握手

  • 爲了防止已失效的鏈接請求報文段忽然又傳送到了服務端,於是產生錯誤

發送 HTTP 請求

TCP 三次握手結束後,開始發送 HTTP 請求報文

其實這部分又能夠稱爲前端工程師眼中的HTTP,它主要發生在客戶端。發送HTTP請求的過程就是構建HTTP請求報文並經過TCP協議發送到服務器指定端口(HTTP協議80/8080, HTTPS協議443)。

請求報文由請求行(request line)、請求頭(header)、請求空行(blank line)、請求體四個部分組成, 以下圖所示:

請求行包含請求方法、URL、協議版本

  • 請求方法包含 8 種:GET、POST、PUT、DELETE、CONNECT、HEAD、OPTIONS、TRACE
  • URL 即請求地址,由 <協議>://<主機>:<端口>/<路徑>?<參數> 組成
  • 協議版本即 http 版本號
POST  /chapter17/user.html HTTP/1.1
複製代碼

以上代碼中「POST」表明請求方法,「/chapter17/user.html」表示 URL,「HTTP/1.1」表明協議和協議的版本。如今比較流行的是 Http1.1 版本

請求頭包含請求的附加信息,由關鍵字/值對組成,每行一對,關鍵字和值用英文冒號「:」分隔。

請求頭部通知服務器有關於客戶端請求的信息。它包含許多有關的客戶端環境請求正文的有用信息。其中好比:Host,表示主機名,虛擬主機;Connection,HTTP/1.1 增長的,使用 keepalive,即持久鏈接,一個鏈接能夠發多個請求;User-Agent,請求發出者,兼容性以及定製化需求。

請求報頭容許客戶端向服務器傳遞請求的附加信息和客戶端自身的信息。 PS: 客戶端不必定特指瀏覽器,有時候也可以使用Linux下的CURL命令以及HTTP客戶端測試工具等。 常見的請求報頭有: Accept, Accept-Charset, Accept-Encoding, Accept-Language, Content-Type, Authorization, Cookie, User-Agent等。

上圖是使用Chrome開發者工具截取的對百度的HTTP請求以及響應報文,從圖中能夠看出,請求報頭中使用了Accept, Accept-Encoding, Accept-Language, Cache-Control, Connection, Cookie等字段。Accept用於指定客戶端用於接受哪些類型的信息,Accept-Encoding與Accept相似,它用於指定接受的編碼方式。Connection設置爲Keep-alive用於告訴客戶端本次HTTP請求結束以後並不須要關閉TCP鏈接,這樣可使下次HTTP請求使用相同的TCP通道,節省TCP鏈接創建的時間。

請求體,能夠承載多個請求參數的數據,包含回車符、換行符和請求數據,並非全部請求都具備請求數據。

當使用POST, PUT等方法時,一般須要客戶端向服務器傳遞數據。這些數據就儲存在請求正文中。在請求頭中有一些與請求正文相關的信息,例如: 如今的Web應用一般採用Rest架構,請求的數據格式通常爲json。這時就須要設置Content-Type: application/json。

name=tom&password=1234&realName=tomson
複製代碼

上面代碼,承載着 name、password、realName 三個請求參數。

服務器處理請求並返回 HTTP 報文

服務器

服務器是網絡環境中的高性能計算機,它偵聽網絡上的其餘計算機(客戶機)提交的服務請求,並提供相應的服務,好比網頁服務、文件下載服務、郵件服務、視頻服務。而客戶端主要的功能是瀏覽網頁、看視頻、聽音樂等等,二者大相徑庭。 每臺服務器上都會安裝處理請求的應用——web server。常見的 web server 產品有 apache、nginx、IIS 或 Lighttpd 等。 web server 擔任管控的角色,對於不一樣用戶發送的請求,會結合配置文件,把不一樣請求委託給服務器上處理相應請求的程序進行處理(例如 CGI 腳本,JSP 腳本,servlets,ASP 腳本,服務器端 JavaScript,或者一些其它的服務器端技術等),而後返回後臺程序處理產生的結果做爲響應。

MVC 後臺處理階段

後臺開發如今有不少框架,但大部分都仍是按照 MVC 設計模式進行搭建的。 MVC 是一個設計模式,將應用程序分紅三個核心部件:模型(model)-- 視圖(view)--控制器(controller),它們各自處理本身的任務,實現輸入、處理和輸出的分離。

  • 視圖(view)

    它是提供給用戶的操做界面,是程序的外殼。

  • 模型(model)

    模型主要負責數據交互。 在 MVC 的三個部件中,模型擁有最多的處理任務。一個模型能爲多個視圖提供數據。

  • 控制器(controller)

    它負責根據用戶從"視圖層"輸入的指令,選取"模型層"中的數據,而後對其進行相應的操做,產生最終結果。 控制器屬於管理者角色,從視圖接收請求並決定調用哪一個模型構件去處理請求,而後再肯定用哪一個視圖來顯示模型處理返回的數據。 這三層是緊密聯繫在一塊兒的,但又是互相獨立的,每一層內部的變化不影響其餘層。每一層都對外提供接口(Interface),供上面一層調用。 至於這一階段發生什麼?簡而言之,首先瀏覽器發送過來的請求先通過控制器,控制器進行邏輯處理和請求分發,接着會調用模型,這一階段模型會獲取 redis db 以及 MySQL 的數據,獲取數據後將渲染好的頁面,響應信息會以響應報文的形式返回給客戶端,最後瀏覽器經過渲染引擎將網頁呈如今用戶面前。

http 響應報文

天然而然這部分對應的就是後端工程師眼中的HTTP。後端從在固定的端口接收到TCP報文開始,這一部分對應於編程語言中的socket。它會對TCP鏈接進行處理,對HTTP協議進行解析,並按照報文格式進一步封裝成HTTP Request對象,供上層使用。這一部分工做通常是由Web服務器去進行,我使用過的Web服務器有Tomcat, Jetty和Netty等等。

響應報文由響應行(request line)、響應頭部(header)、響應主體三個部分組成。 以下圖所示:

響應行包含:協議版本,狀態碼,狀態碼描述

http狀態碼

分類 分類描述
1** 信息,服務器收到請求,須要請求者繼續執行操做
2** 成功,操做被成功接收並處理
3** 重定向,須要進一步的操做以完成請求
4** 客戶端錯誤,請求包含語法錯誤或沒法完成請求
5** 服務器錯誤,服務器在處理請求的過程當中發生了錯誤
響應頭部包含響應報文的附加信息,由 名/值 對組成

常見的響應報頭字段有: Server, Connection...。

響應主體包含回車符、換行符和響應返回數據,並非全部響應報文都有響應數據

服務器返回給瀏覽器的文本信息,一般HTML, CSS, JS, 圖片等文件就放在這一部分。

瀏覽器解析渲染頁面

瀏覽器拿到index.html文件後,就開始解析其中的html代碼,遇到js/css/image等靜態資源時,就向服務器端去請求下載(會使用多線程下載,每一個瀏覽器的線程數不同),這個時候就用上keep-alive特性了,創建一次HTTP鏈接,能夠請求多個資源,下載資源的順序就是按照代碼裏的順序,可是因爲每一個資源大小不同,而瀏覽器又多線程請求請求資源,因此顯示的順序並不必定是代碼裏面的順序。

如遇到圖像,iconfont,JS等的請求時,因爲請求過程是異步的,並不會影響HTML文檔進行加載,可是當文檔加載過程當中遇到JS文件,HTML文檔會掛起渲染過程,不只要等到文檔中JS文件加載完畢還要等待解析執行完畢,纔會繼續HTML的渲染過程。緣由是由於JS有可能修改DOM結構,這就意味着JS執行完成前,後續全部資源的下載是沒有必要的,這就是JS阻塞後續資源下載的根本緣由。CSS文件的加載不影響JS文件的加載,可是卻影響JS文件的執行。JS代碼執行前瀏覽器必須保證CSS文件已經下載並加載完畢。

瀏覽器在請求靜態資源時(在未過時的狀況下),向服務器端發起一個http請求(詢問自從上一次修改時間到如今有沒有對資源進行修改),若是服務器端返回304狀態碼(告訴瀏覽器服務器端沒有修改),那麼瀏覽器會直接讀取本地的該資源的緩存文件。

接下來看一下瀏覽器渲染頁面的過程,以下圖:

瀏覽器解析渲染頁面分爲一下五個步驟:

  • 根據 HTML 解析出 DOM 樹
  • 根據 CSS 解析生成 CSS 規則樹
  • 結合 DOM 樹和 CSS 規則樹,生成渲染樹
  • 根據渲染樹計算每個節點的信息
  • 根據計算好的信息繪製頁面

瀏覽器是一個邊解析邊渲染的過程。首先瀏覽器解析HTML文件構建DOM樹,而後解析CSS文件構建渲染樹,等到渲染樹構建完成後,瀏覽器開始佈局渲染樹並將其繪製到屏幕上。這個過程比較複雜,涉及到兩個概念: reflow(迴流)和repain(重繪)。DOM節點中的各個元素都是以盒模型的形式存在,這些都須要瀏覽器去計算其位置和大小等,這個過程稱爲relow;當盒模型的位置,大小以及其餘屬性,如顏色,字體,等肯定下來以後,瀏覽器便開始繪製內容,這個過程稱爲repain。頁面在首次加載時必然會經歷reflow和repain。reflow和repain過程是很是消耗性能的,尤爲是在移動設備上,它會破壞用戶體驗,有時會形成頁面卡頓。因此咱們應該儘量少的減小reflow和repain。

根據 HTML 解析 DOM 樹

  • 根據 HTML 的內容,將標籤按照結構解析成爲 DOM 樹,DOM 樹解析的過程是一個深度優先遍歷。即先構建當前節點的全部子節點,再構建下一個兄弟節點。
  • 在讀取 HTML 文檔,構建 DOM 樹的過程當中,若遇到 script 標籤,則 DOM 樹的構建會暫停,直至腳本執行完畢。

根據 CSS 解析生成 CSS 規則樹

  • 解析 CSS 規則樹時 js 執行將暫停,直至 CSS 規則樹就緒。
  • 瀏覽器在 CSS 規則樹生成以前不會進行渲染。

結合 DOM 樹和 CSS 規則樹,生成渲染樹

  • DOM 樹和 CSS 規則樹所有準備好了之後,瀏覽器纔會開始構建渲染樹。
  • 精簡 CSS 並能夠加快 CSS 規則樹的構建,從而加快頁面相應速度。

根據渲染樹計算每個節點的信息(佈局)

  • 佈局:經過渲染樹中渲染對象的信息,計算出每個渲染對象的位置和尺寸
  • 迴流:在佈局完成後,發現了某個部分發生了變化影響了佈局,那就須要倒回去從新渲染。

根據計算好的信息繪製頁面

  • 繪製階段,系統會遍歷呈現樹,並調用呈現器的「paint」方法,將呈現器的內容顯示在屏幕上。
  • 重繪:某個元素的背景顏色,文字顏色等,不影響元素周圍或內部佈局的屬性,將只會引發瀏覽器的重繪。
  • 迴流:某個元素的尺寸發生了變化,則需從新計算渲染樹,從新渲染。

詳細的瀏覽器原理:請戳這裏

斷開鏈接

當數據傳送完畢,須要斷開 tcp 鏈接,此時發起 tcp 四次揮手。

  • 發起方向被動方發送報文,Fin、Ack、Seq,表示已經沒有數據傳輸了。並進入 FIN_WAIT_1 狀態。(第一次揮手:由瀏覽器發起的,發送給服務器,我請求報文發送完了,你準備關閉吧)
  • 被動方發送報文,Ack、Seq,表示贊成關閉請求。此時主機發起方進入 FIN_WAIT_2 狀態。(第二次揮手:由服務器發起的,告訴瀏覽器,我請求報文接受完了,我準備關閉了,你也準備吧)
  • 被動方向發起方發送報文段,Fin、Ack、Seq,請求關閉鏈接。並進入 LAST_ACK 狀態。(第三次揮手:由服務器發起,告訴瀏覽器,我響應報文發送完了,你準備關閉吧)
  • 發起方向被動方發送報文段,Ack、Seq。而後進入等待 TIME_WAIT 狀態。被動方收到發起方的報文段之後關閉鏈接。發起方等待必定時間未收到回覆,則正常關閉。(第四次揮手:由瀏覽器發起,告訴服務器,我響應報文接受完了,我準備關閉了,你也準備吧)

Web優化

上面部分主要介紹了一次完整的請求對應的過程,瞭解該過程的目的無非就是爲了Web優化。在談到Web優化以前,咱們回到一個更原始的問題,Web前端的本質是什麼。個人理解是: 將信息快速並友好的展現給用戶並可以與用戶進行交互。快速的意思就是在儘量短的時間內完成頁面的加載,試想一下當你在淘寶購買東西的時候,淘寶頁面加載了10幾秒才顯示出物品,這個時候你還有心情去購買嗎?怎麼快速的完成頁面的加載呢?優雅的學院派雅虎給出了經常使用的一些手段,也就是咱們熟悉的雅虎34條軍規。這34軍規實際上就是圍繞請求過程進行的一些優化方式。

如何儘快的加載資源?答案就是能不從網絡中加載的資源就不從網絡中加載,當咱們合理使用緩存,將資源放在瀏覽器端,這是最快的方式。若是資源必須從網絡中加載,則要考慮縮短鏈接時間,即DNS優化部分;減小響應內容大小,即對內容進行壓縮。另外一方面,若是加載的資源數比較少的話,也能夠快速的響應用戶。當資源到達瀏覽器以後,瀏覽器開始進行解析渲染,瀏覽器中最耗時的部分就是reflow,因此圍繞這一部分就是考慮如何減小reflow的次數。

給你們推薦一個好用的BUG監控工具Fundebug,歡迎免費試用!

參考連接

相關文章
相關標籤/搜索