在瀏覽器輸入URL到整個頁面顯示在用戶面前時這個過程發(fā)生了什么。
總體來說分為以下幾個模塊:
- DNS解析
- TCP連接
- 發(fā)送HTTP請求
- 服務器處理請求并返回HTTP報文
- 瀏覽器解析渲染頁面
- 連接過程
具體流程
DNS解析
DNS解析的過程就是尋找哪臺機器上有你需要資源的過程。當你在瀏覽器輸入一個地址時,例如www.baidu.com,其實不是百度網(wǎng)站真正意義上的地址。互聯(lián)網(wǎng)上每一臺計算機的唯一標識是它的IP地址,但是IP地址并不方便記憶。用戶更喜歡用方便記憶的網(wǎng)址去尋找互聯(lián)網(wǎng)上的其他計算機,也就是上面提到的百度的網(wǎng)址。所以互聯(lián)網(wǎng)設計者需要在用戶的方便性與可用性方面做一個權衡,這個權衡就是一個網(wǎng)址到IP地址的轉換,這個過程就是DNS解析。它實際上充當了一個翻譯的角色,實現(xiàn)了網(wǎng)址到IP的轉換。網(wǎng)址到IP地址轉換的過程是如何進行的?
解析過程
DNS解析是一個遞歸查詢的過程。
首先在本地域名服務器中查詢IP地址,如果沒有找到的情況下,本地域名會向根域名服務器發(fā)送一個請求,如果根域名服務器也不存在該域名時,本地域名會向com頂級域名服務器發(fā)送一個請求,依次類推下去。直到最后本地域名服務器得到google的IP地址并把它緩存到本地,供下次查詢使用。從上述過程中,可以看出網(wǎng)址的解析是一個從右到左的過程:com -> google -> www.google.com。但是你是否發(fā)現(xiàn)了點什么,根域名服務器的解析過程呢?事實上,真正的網(wǎng)址是www.google.com.,并不是我多打了一個“.”,這個對應的就是根域名服務器,默認情況下所有的網(wǎng)址的最后一位都是“.”,既然是默認情況下,為了方便用戶,通常都會省略,瀏覽器在請求DNS的時候會自動加上,所有網(wǎng)址真正的解析過程為: . -> .com -> google.com. ->www.google.com. 。
DNS優(yōu)化
了解了DNS的過程,可以為我們帶來哪些?上文中請求到google的IP地址時,經(jīng)歷了8個步驟,這個過程中存在多個請求(同時存在UDP和TCP請求,為什么有兩種請求方式,請自行查找)。如果每次都經(jīng)過這么多步驟,是否太耗時間?如何減少該過程的步驟呢?那就是DNS緩存。
DNS緩存
DNS存在著多級緩存,從離瀏覽器的距離排序的話,有以下幾種:瀏覽器緩存、系統(tǒng)緩存、路由器緩存、IPS服務器緩存、根域名路由器緩存、頂級域名服務器緩存、主域名服務器緩存。
- 在你的chrome瀏覽器中輸入:chrome://dns/,你可以看到chrome瀏覽器的DNS緩存。
- 系統(tǒng)緩存主要存在/etc/hosts(Linex系統(tǒng))中:
##
# Host Database
#
# localhost is used to configure the llpback interface
# when the system is booting. Do not change this entry。
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
DNS負載均衡
DNS返回的IP地址是否每次都一樣?如果每次都一樣是否說明你請求的資源都位于同一臺機器上面,那么這臺機器需要多高的性能和儲存才能滿足億萬請求呢?其實真實的互聯(lián)網(wǎng)世界背后存在城建上萬臺服務器,大型的網(wǎng)站甚至更多。但是在用戶的眼中,它需要的只是處理他的請求,哪臺機器處理請求并不重要。DNS可以返回一個合適的機器的IP給用戶,例如可以根據(jù)每臺機器的負載量,該機器離用戶地理位置的距離等等,這種過程就是DNS負載均衡,又叫DNS重定向。大家耳熟能詳?shù)腃DN(Content Delivery Network)就是利用DNS的重定向技術,DNS服務器會返回一個跟用戶最接近的點的IP地址給用戶,CDN節(jié)點的服務器負責響應用戶的請求,提供所需的內容。
TCP連接
HTTP協(xié)議是使用TCP作為其傳輸層協(xié)議的,當TCP出現(xiàn)瓶頸時,HTTP也會受到影響。
HTTPS協(xié)議
HTTP報文是包裹在TCP報文中發(fā)送的,服務端受到TCP報文時會解包提取出HTTP報文。但是這個過程中存在一定的風險,HTTP報文是明文,如果中間被截取的話會存在一些信息泄露的風險。那么在進入TCP保溫之前對HTTP做一次加密就可以解決這個問題了。HTTPS協(xié)議的本質就是HTTP+SSL(or TLS)。在HTTP報文進入TCP報文之前,先使用SSL對HTTP報文進行加密。從網(wǎng)絡層結構看它位于HTTP協(xié)議與TCP協(xié)議之間。
HTTPS過程
HTTPS在傳輸數(shù)據(jù)之前需要客戶端與服務器進行一個握手(TLS/SSL握手),在握手過程中將確立雙方加密傳輸數(shù)據(jù)的密碼信息。TLS/SSL使用了非對稱加密,對稱加密以及hash等。HTTPS相比于HTTP,雖然提供了安全保證,但是勢必會帶來一些時間上的損耗,如握手和加密等過程,是否使用了HTTP需要根據(jù)具體情況在安全和性能方面做出權衡。
HTTP請求
這部分又可以稱為前端工程師眼中的HTTP,它主要發(fā)生在客戶端。發(fā)送HTTP請求的過程就是構建HTTP請求報文并通過TCP協(xié)議中發(fā)送到服務器指定端口(HTTP協(xié)議80/8080,HTTP協(xié)議433)。HTTP請求報文是由三部分組成:請求行,請求報頭和請求正文。
請求行
格式如下:Method Request-URL HTTP-VersionCRLF
eg: 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等方法時,通常需要客戶端向服務器傳遞數(shù)據(jù)。這些數(shù)據(jù)就儲存在請求正文中。在請求包頭中有一些與請求正文相關的信息,例如:現(xiàn)在的Web應用通常采用Rest架構,請求的數(shù)據(jù)格式一般為json。這時就需要設置Content-Type: application/json。
服務器處理請求并返回HTTP報文
自然而然這部分對應的就是后端工程師眼中的HTTP。后端從在固定的端口接受到TCP報文開始,這一部分對應于編程語言中的socket。它會對TCP連接進行處理,對HTTP協(xié)議進行解析,并按照報文格式進一步封裝成HTTP Request對象,供上層使用。這一部分工作一般是由web服務器去進行,我使用過的web服務器有Tomcat,Jetty和Netty等等。
HTTP響應報文也是由三部分組成:狀態(tài)碼,響應報頭和響應報文。
狀態(tài)碼
狀態(tài)碼是由3位數(shù)組成,第一個數(shù)字定義了響應的類別,且有5種可能取值:
- 1xx:指示信息-表示請求已接收,繼續(xù)處理。
- 2xx:成功-表示請求已被成功接收、理解、接受。
- 3xx:重定向-要完成請求必須進行更近異步的操作。
- 4xx:客戶端錯誤-請求有語法錯誤或請求無法實現(xiàn)。
- 5xx:服務端錯誤-服務端未能實現(xiàn)合法的請求。平時遇到比較常見的狀態(tài)碼有:200,204,301,302,304,400,401,422,500
響應報頭
常見的響應報頭字段有:Server,Conection
響應報文
服務器返回給瀏覽器的文本信息,通常HTML,CSS,JS,圖片等文件就放在這一部分。
瀏覽器解析渲染頁面
瀏覽器在收到HTML,CSS,JS文件后,它是如何把頁面呈現(xiàn)到屏幕上的?
瀏覽器時一個邊解析邊渲染的過程。首先瀏覽器解析HTML文件構造DOM樹,然后解析CSS文件構建渲染樹等到渲染樹構建完成后,瀏覽器開始布局渲染樹并將其繪制到屏幕上。這個過程比較復雜,設計到兩個概念:reflow(回流)和repain(重繪)。DOM節(jié)點中的各個元素都是以盒模型的形式存在,這些都需要瀏覽器去計算其位置和大小等,這個過程稱為reflow;當盒模型的位置,大小以及其他屬性,如顏色,字體等確定下來之后,瀏覽器便開始繪制內容,這個過程稱為repain。頁面在首次加載時必然會經(jīng)歷reflow和repain。reflow和repain過程是非常消耗性能的,尤其是在移動設備上,它會破壞用戶退演,有時會造成頁面卡頓。所以我們應該盡可能減少reflow和repain。
JS的解析是由瀏覽器中的JS解析引擎完成的。JS是單線程運行,也就是說,在同一個時間內只能做一件事,所有的任務都需要排隊,前一個任務結束,后一個任務才能開始。但是又存在某些任務比較耗時,如IO讀寫等,所以徐亞哦一種機制可以限制性排在后面的任務。這就是:同步任務和異步任務。JS的執(zhí)行機制就可以看做是一個主線程加上一個任務隊列(task queue)。同步任務就是放在主線程上執(zhí)行的任務,異步任務是放在任務隊列中的任務。所有的同步任務在主線程上執(zhí)行,形成一個執(zhí)行棧;異步任務有了運行結果就會在任務隊列中放置一個事件;腳本運行時先依次運行執(zhí)行棧,然后會從任務隊列里提取時間,運行任務隊列中的任務,這個過程是不斷重復的,所以又叫事件循環(huán)(Event loop)。
瀏覽器在解析過程中,如果遇到請求外部資源時,如圖像,iconfont,JS等。瀏覽器將重復1-6過程下載該資源。請求過程是異步的,并不會影響HTML文檔進行加載,但是當文檔加載過程中遇到JS文件,HTML文檔會掛起渲染過程,不僅要等到文檔中JS文件加載完畢還要等待解析執(zhí)行完畢,才會繼續(xù)HTML的渲染過程。原因是因為JS有可能修改DOM結構,這就意味著JS執(zhí)行完成前,后續(xù)所有資源的下載是沒有必要的,這就是JS阻塞后續(xù)資源下載的根本原因。CSS文件的加載不影響JS文件的加載,但是卻影響JS文件的執(zhí)行。JS代碼執(zhí)行前瀏覽器必須保證CSS文件已經(jīng)下載并加載完畢。
web優(yōu)化
上面部分主要介紹了一次完整的請求的過程,了解該過程的目的無非就是為了web優(yōu)化。web需要將信息快速并友好的展示給用戶并能夠與用戶進行交互。快速的意思就是盡可能短的時間內完成頁面的加載,試想一下當你在淘寶購買東西的時候,淘寶頁面加載了10幾秒才顯示出物品,這個時候你還有心情去購買嗎?
如何盡快的加載資源?答案就是能不從網(wǎng)絡中加載的資源就不從網(wǎng)絡中加載,當我們合理使用緩存,將資源放在瀏覽器端,這是最快的方式。如果資源必須從網(wǎng)絡中加載,則要考慮縮短連接時見,即DNS優(yōu)化部分;減少響應內容大小,即對內容進行壓縮。另一方面,如果加載的資源數(shù)比較少的話,也可以快速的響應用戶。當資源到達瀏覽器之后,瀏覽器開始進行解析渲染,瀏覽器中最耗時的部分就是reflow,所以圍繞這一部分就是考慮如何減少reflow的次數(shù)。