總體來說分為以下幾個過程:
-
DNS
解析 -
TCP
連接 - 發送
HTTP
請求 - 服務器處理請求并返回
HTTP
報文 - 瀏覽器解析渲染頁面
- 連接結束
具體過程
DNS解析
DNS
解析的過程就是尋找哪臺機器上有你需要資源的過程。當你在瀏覽器中輸入一個地址時,例如www.baidu.com
,其實不是百度網站真正意義上的地址。互聯網上每一臺計算機的唯一標識是它的IP
地址,但是IP
地址并不方便記憶。用戶更喜歡用方便記憶的網址去尋找互聯網上的其它計算機,也就是上面提到的百度的網址。
所以互聯網設計者需要在用戶的方便性與可用性方面做一個權衡,這個權衡就是一個網址到IP地址的轉換,這個過程就是DNS解析。它實際上充當了一個翻譯的角色,實現了網址到IP
地址的轉換。網址到IP
地址轉換的過程是如何進行的?
解析過程
DNS
解析是一個遞歸查詢的過程。
首先在本地域名服務器中查詢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緩存。
DNS緩存
DNS
存在著多級緩存,從離瀏覽器的距離排序的話,有以下幾種: 瀏覽器緩存,系統緩存,路由器緩存,IPS服務器緩存,根域名服務器緩存,頂級域名服務器緩存,主域名服務器緩存**。
在你的chrome
瀏覽器中輸入:chrome://dns/
,你可以看到chrome
瀏覽器的DNS
緩存。
系統緩存主要存在/etc/hosts
(Linux
系統)中。
DNS負載均衡
不知道大家有沒有思考過一個問題: DNS
返回的IP地址是否每次都一樣?
如果每次都一樣是否說明你請求的資源都位于同一臺機器上面,那么這臺機器需要多高的性能和儲存才能滿足億萬請求呢?其實真實的互聯網世界背后存在成千上百臺服務器,大型的網站甚至更多。但是在用戶的眼中,它需要的只是處理他的請求,哪臺機器處理請求并不重要。
DNS
可以返回一個合適的機器的IP
給用戶,例如可以根據每臺機器的負載量,該機器離用戶地理位置的距離等等,這種過程就是DNS負載均衡,又叫做DNS重定向。
大家耳熟能詳的CDN
(Content Delivery Network
)就是利用DNS
的重定向技術,DNS
服務器會返回一個跟用戶最接近的點的IP
地址給用戶,CDN
節點的服務器負責響應用戶的請求,提供所需的內容。
TCP連接
HTTP
協議是使用TCP
作為其傳輸層協議的,當TCP
出現瓶頸時,HTTP
也會受到影響。但由于TCP
優化這一塊我平常接觸的并不是很多,再加上大學時的計算機網絡的基礎基本上忘完,所以這一部分我也就不在這里分析了。
HTTPS協議
我不知道把HTTPS
放在這個部分是否合適,但是放在這里好像又說的過去。HTTP
報文是包裹在TCP
報文中發送的,服務器端收到TCP
報文時會解包提取出HTTP
報文。但是這個過程中存在一定的風險,HTTP
報文是明文,如果中間被截取的話會存在一些信息泄露的風險。那么在進入TCP
報文之前對HTTP
做一次加密就可以解決這個問題了。
HTTPS
協議的本質就是HTTP
+ SSL
(or TLS
)。在HTTP
報文進入TCP
報文之前,先使用SSL
對HTTP
報文進行加密。從網絡的層級結構看它位于HTTP
協議與TCP
協議之間。
HTTPS過程
HTTPS
在傳輸數據之前需要客戶端與服務器進行一個握手(TLS/SSL
握手),在握手過程中將確立雙方加密傳輸數據的密碼信息。TLS/SSL
使用了非對稱加密,對稱加密以及hash
等。
HTTPS
相比于HTTP
,雖然提供了安全保證,但是勢必會帶來一些時間上的損耗,如握手和加密等過程,是否使用HTTPS
需要根據具體情況在安全和性能方面做出權衡。
HTTP請求
其實這部分又可以稱為前端工程師眼中的HTTP
,它主要發生在客戶端。發送HTTP
請求的過程就是構建HTTP
請求報文并通過TCP
協議中發送到服務器指定端口(HTTP
協議80/8080
, HTTPS
協議443
)。
HTTP
請求報文是由三部分組成: 請求行, 請求報頭和請求正文。
請求行格式如下:
Method Request-URL HTTP-Version CRLF
例:
GET index.html HTTP/1.1
常用的方法有: GET
, POST
, PUT
,DELETE
, OPTIONS
, HEAD
。
請求報頭
請求報頭允許客戶端向服務器傳遞請求的附加信息和客戶端自身的信息。PS: 客戶端不一定特指瀏覽器,有時候也可使用Linux
下的CURL
命令以及HTTP
客戶端測試工具等。
常見的請求報頭有: Accept
, Accept-Charset
, Accept-Encoding
, Accept-Language
, Content-Type
, Authorization
, Cookie
, User-Agent
等。
請求正文
當使用POST
,PUT
等方法時,通常需要客戶端向服務器傳遞數據。這些數據就儲存在請求正文中。在請求包頭中有一些與請求正文相關的信息。例如: 現在的Web
應用通常采用Rest
架構,請求的數據格式一般為json
。這時就需要設置Content-Type: application/json
。
服務器處理請求并返回HTTP
報文
自然而然這部分對應的就是后端工程師眼中的HTTP
。后端從在固定的端口接收到TCP
報文開始,這一部分對應于編程語言中的socket
。它會對TCP
連接進行處理,對HTTP
協議進行解析,并按照報文格式進一步封裝成HTTP Request
對象,供上層使用。這一部分工作一般是由Web
服務器去進行,我使用過的Web
服務器有Tomcat
, Jetty
和Netty
等等。
HTTP
響應報文也是由三部分組成: 狀態碼, 響應報頭和響應報文。
狀態碼
狀態碼是由3位數組成,第一個數字定義了響應的類別,且有五種可能取值:
1xx
:指示信息–表示請求已接收,繼續處理。
2xx
:成功–表示請求已被成功接收、理解、接受。
3xx
:重定向–要完成請求必須進行更進一步的操作。
4xx
:客戶端錯誤–請求有語法錯誤或請求無法實現。
5xx
:服務器端錯誤–服務器未能實現合法的請求。
響應報頭
常見的響應報頭字段有: Server
,Connection
...。
響應報文
服務器返回給瀏覽器的文本信息,通常HTML
,CSS
, JS
, 圖片等文件就放在這一部分。
瀏覽器解析渲染頁面
瀏覽器在收到HTML
,CSS
,JS
文件后,它是如何把頁面呈現到屏幕上的?
瀏覽器是一個邊解析邊渲染的過程。首先瀏覽器解析HTML
文件構建DOM
樹,然后解析CSS
文件構建Style Rules
,通過Attachment
過程之后生成了渲染樹,瀏覽器開始布局渲染樹并將其繪制到屏幕上。這個過程比較復雜,涉及到兩個概念: reflow
(回流)和repain
(重繪)。
DOM
節點中的各個元素都是以盒模型的形式存在,這些都需要瀏覽器去計算其位置和大小等,這個過程稱為relow
;當盒模型的位置,大小以及其他屬性確定下來之后,瀏覽器便開始繪制內容,這個過程稱為repain
。頁面在首次加載時必然會經歷reflow
和repain
。
reflow
和repain
過程是非常消耗性能的,尤其是在移動設備上,它會破壞用戶體驗,有時會造成頁面卡頓。所以我們應該盡可能少的減少reflow
和repain
。
JS
的解析是由瀏覽器中的JS
解析引擎完成的。JS
是單線程運行,也就是說,在同一個時間內只能做一件事,所有的任務都需要排隊,前一個任務結束,后一個任務才能開始。
但是又存在某些任務比較耗時,如IO
讀寫等,所以需要一種機制可以先執行排在后面的任務,這就是:同步任務(synchronous
)和異步任務(asynchronous
)。
JS
的執行機制就可以看做是一個主線程加上一個任務隊列(task queue
)。同步任務就是放在主線程上執行的任務,異步任務是放在任務隊列中的任務。所有的同步任務在主線程上執行,形成一個執行棧;異步任務有了運行結果就會在任務隊列中放置一個事件;腳本運行時先依次運行執行棧,然后會從任務隊列里提取事件,運行任務隊列中的任務,這個過程是不斷重復的,所以又叫做事件循環(Event loop
)。
瀏覽器在解析過程中,如果遇到請求外部資源時,如圖像,iconfont
,JS
等。瀏覽器將需要下載該資源。請求過程是異步的,并不會影響HTML
文檔進行加載,但是當文檔加載過程中遇到JS
文件,HTML
文檔會掛起渲染過程,不僅要等到文檔中JS
文件加載完畢還要等待解析執行完畢,才會繼續HTML
的渲染過程。原因是因為JS
有可能修改DOM
結構,這就意味著JS
執行完成前,后續所有資源的下載是沒有必要的,這就是JS
阻塞后續資源下載的根本原因。CSS
文件的加載不影響JS
文件的加載,但是卻影響JS
文件的執行。JS
代碼執行前瀏覽器必須保證CSS
文件已經下載并加載完畢。
Web優化
上面部分主要介紹了一次完整的請求對應的過程,了解該過程的目的無非就是為了Web
優化。在談到Web
優化之前,我們回到一個更原始的問題,Web
前端的本質是什么。
我的理解是: 將信息快速并友好的展示給用戶并能夠與用戶進行交互??焖俚囊馑季褪窃诒M可能短的時間內完成頁面的加載,試想一下當你在淘寶購買東西的時候,淘寶頁面加載了10
幾秒才顯示出物品,這個時候你還有心情去購買嗎?怎么快速的完成頁面的加載呢?
如何盡快的加載資源?答案就是能不從網絡中加載的資源就不從網絡中加載,當我們合理使用緩存,將資源放在瀏覽器端,這是最快的方式。如果資源必須從網絡中加載,則要考慮縮短連接時間,即DNS
優化部分;減少響應內容大小,即對內容進行壓縮。另一方面,如果加載的資源數比較少的話,也可以快速的響應用戶。當資源到達瀏覽器之后,瀏覽器開始進行解析渲染,瀏覽器中最耗時的部分就是reflow
,所以圍繞這一部分就是考慮如何減少reflow
的次數。