Web 頁面請求過程

一、從輸入一個網址開始

當我們在瀏覽器輸入一個網址,然後按下回車,接下來瀏覽器顯示了頁面。網速好的話這之間可能就一秒,但在這一秒內到底發生了什麼?

本文主要內容是試圖記錄一個完整 Web 請求的詳細過程,從用戶在瀏覽器中輸入 URL 地址說起,然後瀏覽器如何找到服務器地址的過程,併發起請求;分析請求在達反向代理服務器內部處理過程;最後到請求在服務器端處理完成後,瀏覽器渲染響應頁面過程。

大致過程如下:
在這裏插入圖片描述
Web請求的工作原理可以簡單地歸納爲:

  • 瀏覽器通過 DNS 把域名解析成對應的IP地址;

  • 根據這個 IP 地址在互聯網上找到對應的服務器,建立 Socket 連接;

  • 客戶端向服務器發送HTTP協議請求包,請求服務器裏的資源文檔;

  • 在服務器端,實際上還有複雜的業務邏輯:服務器可能有多臺,到底指定哪臺服務器處理請求,這需要一個負載均衡設備來平均分配所有用戶的請求;

  • 還有請求的數據是存儲在分佈式緩存裏還是一個靜態文件中,或是在數據庫裏;

  • 當數據返回瀏覽器時,瀏覽器解析數據發現還有一些靜態資源(如:css,js或者圖片)時又會發起另外的請求,而這些請求可能會在CDN上,那麼CDN服務器又會處理這個用戶的請求。

  • 客戶端與服務器斷開。由客戶端解釋HTML文檔,在客戶端屏幕上渲染圖形結果。

一個 HTTP 事務就是這樣實現的,看起來很簡單,原理其實是挺負責的。需要注意的是客戶機與服務器之間的通信是非持久連接的,也就是當服務器發送了應答後就與客戶機斷開連接,等待下一次請求。

但需要注意的是,從 HTTP 1.1 開始,服務器可以與客戶端保持長連接,不一定是請求完成後就斷開連接,這取決於服務器的操作。

二、DNS 域名解析

首先來看看最先發生的事情——DNS 域名解析,簡單的說就是把域名翻譯成 IP 地址。例如:把 www.test.com 這個域名翻譯成對應 IP 192.168.1.1,這裏只是舉個例子。

如果你在瀏覽器中直接輸入的 IP 地址,那麼實際上會跳過這個步驟,否則會經理下面幾部:

1、瀏覽器緩存檢查

瀏覽器會首先搜索瀏覽器自身的 DNS 緩存,緩存時間比較短,大概只有1分鐘,且只能容納1000條緩存,看自身的緩存中是否有對應的條目,而且沒有過期,如果有且沒有過期則解析到此結束。

2、操作系統緩存檢查 + hosts 解析

如果瀏覽器的緩存裏沒有找到對應的條目,操作系統也會有一個域名解析的過程,那麼瀏覽器先搜索操作系統的 DNS 緩存中是否有這個域名對應的解析結果,如果找到且沒有過期則停止搜索,解析到此結束。

在 Linux 中可以通過 /etc/hosts 文件來設置,可以將任何域名解析到任何能夠訪問的 IP 地址。如果在這裏指定了一個域名對應的 IP 地址,那麼瀏覽器會首先使用這個 IP 地址。當解析到這個配置文件中的某個域名時,操作系統會在緩存中緩存這個解析結果,緩存的時間同樣是受這個域名的失效時間和緩存的空間大小控制的。

3、本地區域名服務器(Local DNS Server)解析

如果在 hosts 文件中也沒有找到對應的條目,瀏覽器會發起一個 DNS 的系統調用,會向本地配置的首選 DNS 服務器發起域名解析請求(通過的是 UDP 協議向 DNS 的 53 端口發起請求,這個請求是遞歸的請求,也就是運營商的DNS服務器必須得提供給我們該域名的IP地址)。

在我們的網絡配置中都會有“DNS 服務器地址”這一項,這個地址就用於解決前面所說的如果兩個過程無法解析時要怎麼辦。操作系統會把這個域名發送給這裏設置的 LDNS,也就是本地區的域名服務器。

這個 DNS 通常都提供給你本地互聯網接入的一個 DNS 解析服務,例如你是在學校接入互聯網,那麼你的 DNS 服務器肯定在你的學校;如果你是在一個小區接入互聯網的,那這個 DNS 就是提供給你接入互聯網的應用提供商,即電信或者聯通。大約 80% 的域名解析都到這裏就已經完成了,所以 LDNS 主要承擔了域名的解析工作。

4、根域名服務器解析(Root Server)

如果 LDNS 沒有找到對應的條目,則由運營商的 DNS 代我們的瀏覽器發起迭代 DNS 解析請求。它首先是會找根域的 DNS 的 IP 地址,找到根域的 DNS 地址,就會向其發起請求。然後根域名服務器返回給本地域名服務器一個所查詢域的主域名服務器(gTLD Server)地址。

5、主域名服務器(gTLD Server)

本地域名服務器(LDNS Server)再向上一步返回的 gTLD 服務器發送請求。

接受請求的 gTLD 服務器查找並返回此域名對應的 Name Server 域名服務器的地址,這個 Name Server 通常就是你註冊的域名服務器,例如你在某個域名服務提供商申請的域名,那麼這個域名解析任務就由這個域名提供商的服務器來完成。

Name Server 域名服務器會查詢存儲的域名和IP的映射關係表,正常情況下都根據域名得到目標IP記錄,連同一個 TTL 值返回給 DNS Server 域名服務器。

下圖彙總了上面所說的 DNS 解析過程:
在這裏插入圖片描述
三、TCP 的 3 次握手

拿到域名對應的 IP 地址後,User-Agent(一般是指瀏覽器)會以一個隨機端口(1024 < 端口 < 65535)向服務器的 WEB 程序發起 TCP 的連接請求。

這裏還涉及 ARP(地址解析協議):是根據 IP 地址獲取物理地址 (MAC 地址) 的一個協議。
當一個數據幀經過多次路由到達目的網絡時,路由器只能知道其數據幀中的目的 IP 地址,而不知目標主機的硬件地址,網絡層使用的是 IP地址,但是在實際網絡鏈路上傳送數據幀時,最終必須使用該網絡的硬件地址,此時需要目的主機的硬件地址,就要使用 ARP 來獲取到對應 IP 地址主機的物理地址。

這個連接請求(原始的 Http 請求經過 TCP/IP 4層模型的層層封包)到達服務器端後(這中間通過各種路由設備,局域網內除外),進入到網卡,然後是進入到內核的 TCP/IP 協議棧(用於識別該連接請求,解封包,一層一層的剝開),還有可能要經過Netfilter防火牆(屬於內核的模塊)的過濾,最終到達WEB程序,最終建立了TCP/IP的連接。

在這裏插入圖片描述

1、 Client 首先發送一個連接試探,SYN = 1 表示這是一個連接請求或連接接受報文,同時表示這個數據報不能攜帶數據,seq = x 表示 Client 自己的初始序號(seq = 0 就代表這是第 0 號包),這時候 Client 進入 syn_sent 狀態,表示客戶端等待服務器的回覆。

2、 Server 監聽到連接請求報文後,如同意建立連接,則向 Client 發送確認。報文中的 SYN 和 ACK 都置 1 ,ACK = x + 1 表示期望收到對方下一個報文段的第一個數據字節序號是 x+1,同時表明 x 爲止的所有數據都已正確收到(ACK = 1 其實是 ACK = 0 + 1,也就是期望客戶端的第 1 個包),seq = y 表示 Server 自己的初始序號(seq = 0 就代表這是服務器這邊發出的第 0 號包)。這時服務器進入 syn_rcvd,表示服務器已經收到 Client 的連接請求,等待確認。

3、 Client 收到確認後還需再次發送確認,同時攜帶要發送給 Server 的數據。ACK 置 1 表示確認號 ack= y + 1 有效(代表期望收到服務器的第 1 個包),Client自己的序號 seq= x + 1(表示這就是我的第1個包,相對於第0個包來說的),一旦收到Client的確認之後,這個TCP連接就進入 Established 狀態,就可以發起請求了。

四、Nginx 反向代理

1、反向代理

反向代理(Reverse Proxy)方式是指:代理服務器來接受 Internet 上的連接請求,然後將請求轉發給內部網絡上的服務器,並將從內部網絡上服務器得到的結果返回給 Internet 上請求連接的客戶端。此時代理服務器對外就表現爲一個服務器,反向代理服務器對於客戶端而言它就像是原始服務器,並且客戶端不需要進行任何特別的設置。

反向代理的作用:

(1)保證內網的安全,可以使用反向代理提供 WAF 功能,阻止 web 攻擊。

(2)負載均衡,通過反向代理服務器來優化網站的負載。

2、正向代理

既然有反向代理,就肯定有正向代理。什麼叫正向代理呢?

正向代理(Forward Proxy)通常都被簡稱爲代理,就是在用戶無法正常訪問外部資源,可以通過代理的方式,讓用戶繞過防火牆,從而連接到目標網絡或者服務。

正向代理的工作原理就像一個跳板。

比如:我訪問不了 google.com,但是我能訪問一個代理服務器 A,A 能訪問 google.com,於是我先連上代理服務器 A,告訴它我需要 google.com 的內容,A 就去取回來,然後返回給我。

從網站的角度,只在代理服務器來取內容的時候有一次記錄,有時候並不知道是用戶的請求,也隱藏了用戶的資料,這取決於代理告不告訴網站。

正向代理是一個位於客戶端和原始服務器(origin server)之間的服務器。爲了從原始服務器取得內容,客戶端向代理髮送一個請求並指定目標(原始服務器),然後代理向原始服務器轉交請求並將獲得的內容返回給客戶端。

3、正向代理與反向代理對比

在這裏插入圖片描述

五、關閉 TCP 連接

這一步不是所有的網頁都會這麼做,例如網頁版微信就沒有關閉 TCP 連接,因爲微信上別人可以隨時發消息給你,實際上別人先把消息發送到了微信服務器,微信服務器再通過 TCP 鏈接,把消息推送到你的屏幕上。

試想一下,如果網頁版微信關閉了 TCP 連接會怎樣?

結果是:你不刷新網頁,就永遠收不到消息了。同時,如果你頻繁的發消息給別人,那麼就在頻繁的創建連接,關閉連接,這是很消耗資源的。所以微信就乾脆不關閉 TCP 連接,這樣微信服務器就可以給我們的瀏覽器發消息。

下圖是一次 Http 請求報文頭部信息,其中 Connection: keep-alive 意味着這次請求結束後不會關閉 TCP 連接。

在這裏插入圖片描述

當然不是所有的 HTTP 請求都沒有關閉連接,例如一篇博文,瀏覽器收到數據顯示就可以了,沒有那麼多動態數據,我看完就關了,這時就應該關閉 TCP 連接,當然這還是取決於請求的服務器。說了這麼多,還沒說關閉連接。

關閉 TCP 連接專業點說叫做“四次揮手”,與 TCP 建立連接的“三次握手”相對應。

由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個 FIN 來終止這個方向的連接。收到一個 FIN 只意味着這一方向上沒有數據流動,一個TCP連接在收到一個 FIN 後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章