天天看點

浏覽器位址欄輸入url 到頁面生成發生了什麼DNS解析TCP連接配接發送HTTP請求服務端響應HTTP請求HTTP緩存渲染頁面連接配接關閉

  1. 輸入位址;
  2. 根據位址欄輸入的位址發生DNS 解析并查找IP;
  3. 通過IP位址與伺服器建立TCP 連接配接;
  4. 用戶端向服務端發送http 請求,如果傳回301 之類的重定向,浏覽器根據根據響應頭中的location再次發送請求;
  5. 伺服器收到請求并響應HTTP 請求,處理請求,生成html 代碼,傳回給浏覽器
  6. 浏覽器開始解析渲染頁面并顯示
  7. 關閉連接配接  

 輸入位址

當我們開始在浏覽器中輸入網站時,浏覽器其實就已經在智能的比對一些可能的url了,它會從書簽、曆史記錄等地方找到已經輸入的url字元串所對應的url,然後給出智能提示,讓你可以補全url位址。對于google的chrome浏覽器,它甚至會從緩存中把網頁展示出來;換言之當我們輸入位址時,頁面就出來了。

DNS解析

DNS(Domain Name System)服務是和HTTP協定一樣位于應用層的協定。它提供域名到IP位址之間的解析。使用者通常使用主機名或域名來通路對方的計算機,而不是通過IP位址通路。

DNS解析的過程就是尋找哪台機器上有你需要資源的過程。當你在浏覽器中輸入一個位址時,例如www.baidu.com,其實不是百度網站真正意義上的位址。網際網路上每一台計算機的唯一辨別是它的IP位址,但是IP位址并不友善記憶。使用者更喜歡用友善記憶的網址去尋找網際網路上的其它計算機,也就是上面提到的百度的網址。是以網際網路設計者需要在使用者的友善性與可用性方面做一個權衡,這個權衡就是一個網址到IP位址的轉換,這個過程就是DNS解析。它實際上充當了一個翻譯的角色,實作了網址到IP位址的轉換。

什麼是DNS

域名系統(英文:Domain Name System,縮寫:DNS)是網際網路的一項服務。它作為将域名和IP位址互相映射的一個分布式資料庫,能夠使人更友善地通路網際網路。DNS使用TCP和UDP端口53。目前,對于每一級域名長度的限制是63個字元,域名總長度則不能超過253個字元。 --維基百科

域名解析的過程是逐級查詢的

  1. 浏覽器緩存: 首先會向浏覽器的緩存中讀取上一次通路的記錄,在chrome可以通過位址欄中輸入chrome://net-internals/#dns檢視緩存的目前狀态
  2. 作業系統緩存: 查找存儲在系統運作記憶體中的緩存。在mac中可以通過下面的指令清除系統中的DNS緩存。
dscacheutil -flushcache
           
  1. 在host檔案中查找:如果在緩存中都查找不到的情況下,就會讀取系統中預設的host檔案中的設定。
  2. 路由器緩存:有些路由器也有DNS緩存的功能,通路過的域名會存在路由器上。
  3. ISP DNS緩存:網際網路服務提供商(如中國電信)也會提供DNS服務,比如比較著名的 114.114.114.114,在本地查找不到的情況下,就會向ISP進行查詢,ISP會在目前伺服器的緩存内查找是否有記錄,如果有,則傳回這個IP,若沒有,則會開始向根域名伺服器請求查詢。
  4. 頂級DNS伺服器/根DNS伺服器:根域名收到請求後,會判别這個域名(.com)是授權給哪台伺服器管理,并傳回這個頂級DNS伺服器的IP。請求者收到這台頂級DNS的伺服器IP後,會向該伺服器發起查詢,如果該伺服器無法解析,該伺服器就會傳回下一級的DNS伺服器IP(nicefilm.com),本機繼續查找,直到伺服器找到(www.nicefilm.com)的主機。

 我們可以通過dig指令檢視域名解析的記錄

dig math.stackexchange.com
           

我們重點看傳回的應答,會看到有四條記錄,傳回了該網址的四個IP 

;; ANSWER SECTION:
math.stackexchange.com.	34	IN	A	151.101.129.69
math.stackexchange.com.	34	IN	A	151.101.65.69
math.stackexchange.com.	34	IN	A	151.101.1.69
math.stackexchange.com.	34	IN	A	151.101.193.69
           

34是TTL的值,表示該域名的緩存時間,即該時間内不用重新查詢。A是該DNS查詢的記錄類型,表示傳回一個IPv4格式的位址。還有其他記錄類型諸如 NS(傳回查詢的伺服器位址)、AAAA(傳回IPV6格式的位址)、CNAME(域名的别名)等。

 解析過程

 DNS解析是一個遞歸查詢的過程。

浏覽器位址欄輸入url 到頁面生成發生了什麼DNS解析TCP連接配接發送HTTP請求服務端響應HTTP請求HTTP緩存渲染頁面連接配接關閉

上述圖檔是查找www.google.com的IP位址過程。首先在本地域名伺服器中查詢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連接配接

TCP 是一種面向有連接配接的傳輸層協定。 它可以保證兩端(發送端和接收端)通信主機之間的通信可達。 它能夠處理在傳輸過程中丢包、傳輸順序亂掉等異常情況;此外它還能有效利用寬帶,緩解網絡擁堵。

拿到了要請求的資源伺服器IP後,浏覽器通過操作OS的socket與伺服器進行TCP連接配接(一般來說作業系統已經封裝好了TCP/IP等協定,提供套接字給應用去使用,該部分涉及到标準網絡模型的知識,另外再開篇拓展。)

這個連接配接就是我們所熟知的三次握手 本機主動打開連接配接

  • 第一次,本機将辨別位 SYN 置為 1, seq = x(Sequence number)發送給服務端。此時本機狀态為SYN-SENT
  • 第二次,伺服器收到包之後,将狀态切換為SYN-RECEIVED,并将辨別位 SYN 和 ACK都置為1, seq = y, ack = x + 1, 并發送給用戶端。
  • 第三次,用戶端收到包後,将狀态切換為ESTABLISHED,并将辨別位ACK置為1,seq = x + 1, ack = y + 1, 并發送給服務端。服務端收到包之後,也将狀态切換為ESTABLISHED。

需要注意的一點是,有一些文章對ACK辨別位 和 ack(Acknowledgement Number)的解釋比較模糊,有一些畫圖的時候幹脆就寫在一起了。雖然這兩者有關聯,但不是同一個東西,搞清楚這個誤區可以更友善去了解。還有一些會把第二次握手描述成兩個包(比如某百科……),實際上這也是不正确的

  • 辨別位ACK置為1 表示我已确認收到seq為x的包,并回複确認序号ack = x + 1
  • 而SYN表示這是我第一次随機生成seq的序列x,此後我每次發送的包都會在上一次發送的基礎上增加y(有資料的時候,y是資料的長度,沒有的時候y = 1)。是以,當seq已初始化完成之後,沒必要再把SYN置為1 了解了這兩點,也就不難了解為什麼三次握手分别是SYN、ACK/SYN、ACK了。

辨別位(TCP FLAG) TCP的頭部固定有20個位元組,其中配置設定了6bits給TCP FLAG,組合起來用來表示目前包的類型。分别是 URGACKPSHRSTSYNFIN(CWRECE放在保留位,暫不考慮)

  • URG:緊急指針,用于将要發送的包辨別為“緊急”,這意味着不必等待前段資料被響應處理完即可發送給接收端。
  • ACK:确認辨別,用于表示對資料包的成功接收。
  • PSH:推送辨別,表示這個資料包應該被立即發送,不需要等待額外的資料。
  • RST:reset辨別,用來異常關閉連接配接。
  • SYN:同步辨別,表示TCP連接配接已初始化。
  • FIN:完成辨別,用于拆除上一個SYN辨別。一個完整的TCP連接配接過程一定會有SYN 和 FIN包。 至此我們了解了一個TCP 連接配接的過程,通道通了,是時候利用這個通道送東西了。 我們從傳輸層再回到應用層。

發送HTTP請求

超文本傳輸協定(英文:HyperText Transfer Protocol,縮寫:HTTP)是一種用于分布式、協作式和超媒體資訊系統的應用層協定[1]。HTTP是網際網路的資料通信的基礎。設計HTTP最初的目的是為了提供一種釋出和接收HTML頁面的方法。通過HTTP或者HTTPS協定請求的資源由統一資源辨別符(Uniform Resource Identifiers,URI)來辨別。

在應用層,浏覽器會分析這個url,并設定好請求封包發出。請求封包中包括請求行、請求頭、空行、請求主體。https預設請求端口443, http預設80。

  • 請求行:請求行中包括請求的方法,路徑和協定版本。
  • 請求頭:請求頭中包含了請求的一些附加的資訊,一般是以鍵值的形式成對存在,比如設定請求檔案的類型accept-type,以及伺服器對緩存的設定。
  • 空行:協定中規定請求頭和請求主體間必須用一個空行隔開
  • 請求主體:對于post請求,所需要的參數都不會放在url中,這時候就需要一個載體了,這個載體就是請求主題。

服務端響應HTTP請求

在接收和解釋請求消息後,伺服器傳回一個HTTP響應消息。 HTTP 響應由三個部分組成,分别是:狀态行、消息報頭、響應正文。 狀态代碼:由三位數字組成,第一個數字定義了響應的類别,且有五種可能取值:

  • 1xx:訓示資訊--表示請求已接收,繼續處理
  • 2xx:成功--表示請求已被成功接收、了解、接受
  • 3xx:重定向--要完成請求必須進行更進一步的操作
  • 4xx:用戶端錯誤--請求有文法錯誤或請求無法實作
  • 5xx:伺服器端錯誤--伺服器未能實作合法的請求

常見狀态代碼、狀态描述、說明:

  • 200 OK :用戶端請求成功
  • 400 Bad Request :用戶端請求有文法錯誤,不能被伺服器所了解
  • 401 Unauthorized :請求未經授權,這個狀态代碼必須和WWW-Authenticate報頭域一起使用
  • 403 Forbidden :伺服器收到請求,但是拒絕提供服務
  • 404 Not Found :請求資源不存在,eg:輸入了錯誤的URL
  • 500 Internal Server Error :伺服器發生不可預期的錯誤
  • 503 Server Unavailable :伺服器目前不能處理用戶端的請求,一段時間後可能恢複正常

HTTP緩存

HTTP 消息報頭包括:普通報頭、請求報頭、響應報頭、實體報頭。具體不作介紹。 響應正文:就是伺服器傳回的資源的内容

http緩存 請求是浏覽器的一個優化點,我們可以通過緩存來減少不必要的請求,進而加快頁面的呈現。通過簡單地設定http頭部可以使用緩存的功能。一般來說有三種設定的方式

Last-Modify(響應頭) + If-Modified-Since(請求頭) 伺服器在傳回資源的時候設定Last-Modify目前資源最後一次修改的時間,浏覽器會把這個時間儲存下來,在下次請求的時候,請求頭部If-Modified-Since 會包含這個時間,服務端收到請求後,會比對資源最後更新的時間是否在If-Modified-Since設定的時間之後,如果不是,傳回304狀态碼,浏覽器将從緩存中擷取資源。反之傳回200和資源内容。

ETag(響應頭) + If-None-Match(請求頭) 根據資源辨別符來确定檔案是否存在修改,伺服器每一次傳回資源,都會在Etag中存放資源的辨別符,浏覽器收到這個辨別符,在下一次請求的時候将辨別符放在If-None-Match中,服務端将判斷是否比對,如果不比對,傳回200以及新的資源,反之傳回304,浏覽器從緩存中擷取資源

Cache-Control/Expires(響應頭) 首先這不是一種方法,而是協定更替中的一種演化。 在http 1.0的時代,我們基于Pragma 和 Expires 控制緩存的生命周期。我們可以通過設定Pragma為no-cache關閉緩存功能,同樣也可以在Expires中設定一個緩存失效的時間。需要注意的是,這個失效的時間是相對于伺服器的實踐而言的,如果人為地改變了用戶端的時間,是會導緻緩存失效的。

是以,為了解決這個問題,HTTP1.1的協定加入了Cache-Control,通過設定Cache-Control的max-age可以控制緩存的周期。在這個周期内,資源是新鮮的,浏覽器再一次需要使用資源的時候,就不會送出請求了。

渲染頁面

在浏覽器沒有完整接受全部 HTML 文檔時,它就已經開始顯示這個頁面了,不同浏覽器可能解析的過程不太一樣,這裡我們隻介紹 WebKit 的渲染過程。

  1. 解析HTML,建構 DOM 樹
  2. 解析 CSS ,生成 CSS 規則樹
  3. 合并 DOM 樹和 CSS 規則,生成 render 樹
  4. 布局 render 樹( Layout / reflow ),負責各元素尺寸、位置的計算
  5. 繪制 render 樹( paint ),繪制頁面像素資訊
  6. 浏覽器會将各層的資訊發送給 GPU,GPU 會将各層合成( composite ),顯示在螢幕上

需要注意的是,這是一個漸進的過程,呈現引擎為了力求顯示的及時,會在文檔請求不完全的情況下就開始渲染頁面,同時,如果在解析的過程中遇到script的時候,文檔的解析将會停止下來,立即解析執行腳本,如果腳本是外部的,則會等待請求完成并解析執行。是以,為了不阻塞頁面地呈現,一般會把script腳本放在文檔的最後。

在最新的HTML4和HTML5規範中,也可以将腳本标注為defer,這樣就不會停止文檔解析,而是等到解析結束後才執行。HTML5 增加了一個選項,可将腳本标記為async,以便由其他線程解析和執行。

連接配接關閉

現在的頁面為了優化請求的耗時,預設都會開啟持久連接配接(keep-alive),那麼一個TCP連接配接确切關閉的時機,是這個tab标簽頁關閉的時候。這個關閉的過程就是著名的四次揮手。關閉是一個全雙工的過程,發包的順序的不一定的。一般來說是用戶端主動發起的關閉,過程如下。

假如最後一次用戶端發出的資料seq = x, ack = y;

  • 用戶端發送一個FIN置為1的包,ack = y, seq = x + 1,此時用戶端的狀态為 FIN_WAIT_1
  • 服務端收到包後,狀态切換為CLOSE_WAIT發送一個ACK為1的包, ack = x + 2。用戶端收到包之後狀态切換為FNI_WAIT_2
  • 服務端處理完任務後,向用戶端發送一個 FIN包,seq = y; 同時将自己的狀态置為LAST_ACK
  • 用戶端收到包後狀态切換為TIME_WAIT,并向服務端發送ACK包,ack = y + 1,等待2MSL後關閉連接配接。 為什麼用戶端等待2MSL? MSL: 全程Maximum Segment Lifetime,中文可以翻譯為封包最大生存時間。 等待是為了保證連接配接的可靠性,確定服務端收到ACK包,如果服務端沒有收到這個ACK包,将會重發FIN包給用戶端,而這個時間剛好是服務端等待逾時重發的時間 + FIN的傳輸時間。