【譯】從輸入URL到頁面渲染完成

以下是我意譯加直譯了原文作者的文章內容,翻譯得不好,還請多多指教。
原文鏈接:從輸入URL到頁面渲染完成


作爲一個軟件開發者,你一定會對網絡應用如何工作有一個完整的層次化的認知,同樣這裏也包括這些應用所用到的技術:像瀏覽器,HTTP,HTML,網絡服務器,需求處理等等。

本文將更深入的研究當你訪問一個URL時,到底發生了一件件什麼樣的事。

1.輸入URL地址

一切將從這裏開始

輸入URL

2.瀏覽器根據域名查詢IP地址

DNS查詢

第一步是根據一定步驟從要訪問的域名中獲取IP地址,DNS查詢的步驟如下:

1. 從瀏覽器緩存中查詢。瀏覽器會存儲一定時間的DNS記錄,操作系統不會告訴瀏覽器每個DNS記錄的保存時限,不同瀏覽器設置保存時限爲一個固定值(不同瀏覽器情況不同,一般在2-30分鐘)。
2. 從操作系統緩存中查詢。如果瀏覽器中沒有包含想要的緩存記錄,那瀏覽器就會發起操作系統請求,繼續查詢操作系統緩存
3. 從路由器中查詢DNS緩存。請求持續發送到你的路由,它通常會有自己的DNS緩存。
4. 從ISP中查詢DNS緩存。下一個被查詢地方是ISP緩存DNS的服務器。
5. 域名服務器遞歸查詢。首先從root域名服務器中查詢如.com域名服務器,然後逐步向前查詢,.com頂級域名服務器到Facebook的域名服務器。一般來說,.com級別的都已經在緩存中了,所以一般不會進行對root域名服務器的查詢。下面給出一張遞歸查詢的圖。

a diagram of what a recursive DNS search looks like

往往像wikipedia.org或者facebook.com整個域都映射到不止一個IP地址,幸運的是我們有一些解決方法

- Round-robin DNS,DNS輪詢是其中一種方法,是DNS查找時返回多個IP時的解決方案。舉例來說,Facebook.com實際上就對應了四個IP地址。
- Load-balancer,大型的網站一般都會使用高性能的負載均衡器來平衡流量。負載均衡器一直監聽一個特殊的IP地址,並轉發請求到其他的服務器。(譯者注:簡單粗暴點理解就是在用戶和服務器之間加了箇中間層,利用監聽和轉發請求達到用戶相對快速訪問,資源最優化使用的目的)
- Geographic DNS,基於地理的DNS,依賴客戶端的地理位置,這是一個很好的存儲靜態資源的方法,不同的服務器可以不更新共享狀態。
- Anycast,一種單個IP地址映射多個物理服務器的技術。

3. 瀏覽器發送HTTP請求到web服務器

發送HTTP請求

因爲Facebook的首頁是動態變化的,所以我們肯定無法從瀏覽器緩存中獲取整個首頁。所以瀏覽器將會發送請求到Facebook的服務器

GET http://facebook.com/ HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: facebook.com
Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]

向“http://facebook.com/”發出GET請求,瀏覽器用User-Agent header說明自己的身份,Accept和Accept-Encoding header表明其接收何種形式的響應,Connection header要求服務器爲了進一步的請求發送保持TCP連接。
請求當然少不了瀏覽器在這個域下保存的cookies,cookies是保留不同網站狀態的鍵值對,常常保存着用戶登錄信息,用戶在服務器保存的密碼,一些用戶設置等等。cookies將會被保存在客戶端的一個文件裏,它會被每次請求帶到服務器。

看原始HTTP請求及其相應的工具很多。我最喜歡使用的是fiddler,當然也有像FireBug這樣其他的工具。這些軟件在網站優化時會幫上很大忙。

除了獲取請求,還有一種是發送請求,它常在提交表單用到。發送請求通過URL傳遞其參數(e.g.: http://robozzle.com/puzzle.aspx?id=85)。發送請求在請求正文頭之後發送其參數。

在URL末尾的"/"是很重要的,在"http://facebook.com/"例子中,瀏覽器能夠安全的添加"/"。但對於表單的URLs"http://example.com/folderOrFile"來說,瀏覽器不能自動添加斜槓,因爲並不清楚folderOrFile是一個文件夾還是文件,在這樣的例子中,瀏覽器將會先不帶"/"訪問這個URL,然後服務器會迴應一個重定向,導致一個不必要通信來回。

4. Facebook服務器返回一個永久重定向響應

返回永久重定向

Facebook服務器會迴應瀏覽器請求發送回一個響應

HTTP/1.1 301 Moved Permanently
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
  pre-check=0
Expires: Sat, 01 Jan 2000 00:00:00 GMT
Location: http://www.facebook.com/
P3P: CP="DSP LAW"
Pragma: no-cache
Set-Cookie: made_write_conn=deleted; expires=Thu, 12-Feb-2009 05:09:50 GMT;
  path=/; domain=.facebook.com; httponly
Content-Type: text/html; charset=utf-8
X-Cnection: close
Date: Fri, 12 Feb 2010 05:09:51 GMT
Content-Length: 0

服務器會發送一個301永久重定向響應來告訴瀏覽器訪問“http://www.facebook.com/而不是“http://facebook.com/”。爲什麼服務器堅持重定向而不是直接給予瀏覽器用戶需要的結果,這有很多有意思原因。

一個原因是搜索引擎排名,如果有兩個URLs指向同一個頁面,比如http://www.igoro.com/ 和http://igoro.com/,搜索引擎會認爲這是兩個不同的網站,結果他們兩個每個都有一部分訪問量,但是也只能擁有更低的搜索引擎排名。如果使用了301定位,搜索引擎將會識別重定向,進而將同一來源的多個鏈接算作一個。

另一個原因是,同樣的內容多個URLs還不利於緩存,同樣的內容擁有多個名字,潛在造成緩存浪費。

5.瀏覽器會跟蹤重定向地址

跟蹤重定向

瀏覽器知道了“http://www.facebook.com/” 是真正應該訪問的URL,所以就發送了另外一個GET請求

GET http://www.facebook.com/ HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Cookie: lsd=XW[...]; c_user=21[...]; x-referer=[...]
Host: www.facebook.com

這個頭的含義與第一個請求相同

6.服務器處理請求

服務器處理請求

服務器會接收這個GET請求,並且返回一個響應結果

這可能看着像一個很簡單的任務,但即使我們在訪問一個很簡單網站(像我們的博客一樣),在這個過程中實際上也會發生很多有趣的事情,更不用說訪問Facebook這樣大訪問量的網站了

  • web服務器軟件

    web服務器軟件(像apache和IIS等)接收HTTP請求並決定使用哪個請求處理程序處理這個請求。一個請求處理程序是能夠讀取請求並且爲響應結果生成HTML的一段程序(ASP.NET, PHP, Ruby, …)
    在最簡單的例子中,這個請求處理程序會儲存在一個文件中,而這個文件的分佈會像URL的結構的投影一樣,比如說http://example.com/folder1/page1.aspx 這個URL就映射着路徑爲/httpdocs/folder1/page1.aspx的文件,web服務器軟件能配置爲將每個URLs手動映射到請求處理程序,所以page1.aspx的公有URL應該是http://example.com/folder1/page1.
    
  • 請求處理程序

    請求處理程序讀取請求和參數以及cookies。它會讀取以及可能更新一些存儲在服務器的數據,接着請求處理程序會生成一個HTML響應

怎麼存儲數據是每個動態網站都會面臨的有趣難題

小網站會經常有一個SQL數據庫來存儲他們的數據,但是網站存儲數據量過大或者流量過大後就必須將數據庫分佈在多臺機器,解決的方法有很多種

包括sharding(在主鍵基礎上劃分表到多個數據庫中),複製和使用簡化的弱語義一致性數據庫

推遲一些任務到批處理作業是廉價保持數據更新的的一種技術。

例如,Facebook必須儘快更新新聞供應,但數據支持的“你可能認識的人”功能可能只需要每晚進行更新(作者猜測是這樣)。批處理作業的更新導致存在一些舊的相對不重要的數據,但是使數據更新更快更簡單。

7.服務器返回一個HTML響應

返回響應

這裏有一個服務器生成和送回的響應

HTTP/1.1 200 OK
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
    pre-check=0
Expires: Sat, 01 Jan 2000 00:00:00 GMT
P3P: CP="DSP LAW"
Pragma: no-cache
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
X-Cnection: close
Transfer-Encoding: chunked
Date: Fri, 12 Feb 2010 09:05:55 GMT
...

整個完整的響應是36KB,其中大部分處理後由blob類型傳送
內容編碼頭部告訴瀏覽器響應體使用了gzip壓縮算法。在解析blob後,你就會看到你期望的HTML了

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"   
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" 
      lang="en" id="facebook" class=" no_js">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-language" content="en" />
...

除了壓縮信息之外,頭部還詳細說明了是否和怎麼緩存頁面、設置cookies(在這個響應中沒有)、隱祕信息等

或許有人注意到了設置了內容類型爲text/html,這部分頭部說明了瀏覽器將響應內容作爲HTML渲染,而不是作爲文件下載。瀏覽器將使用頭部決定如何解釋響應結果,當然也會考慮其他因素,比如URL的擴展情況

8.瀏覽器開始渲染HTML

在瀏覽器接收完整HTML文件前,瀏覽器就開始渲染頁面了

渲染頁面

9.瀏覽器發送嵌入在HTML中的對象的請求

請求和響應

隨着瀏覽器渲染HTML,瀏覽器會注意到有些標籤需要請求其他URLs的資源,瀏覽器將會發送一個GET請求來重新獲取每個文件
下面是一些我們訪問Facebook.com獲取的文件

每個URLs會像獲取HTML頁面的過程一樣獲取相應資源。所以,瀏覽器會在DNS中查詢域名,並向URL發送請求,進行重定向等等以上步驟

當然,靜態文件和動態網站不一樣,它們允許被瀏覽器緩存。一些文件可能會根本不經過服務器,直接被從緩存中取出。因爲響應結果中返回一個包含着Expires頭的文件,所以瀏覽器知道要緩存一個文件多久。另外每個響應可能包含着ETag頭,其作用類似版本號,如果瀏覽器發現已經擁有了一個文件的ETag,那麼就會立即停止此文件傳輸。

你能猜到“fbcdn.net”在這個URLs代表着什麼嗎?一個保險的說法是“Facebook內容分發網絡”。Facebook使用CDN來部署靜態資源--圖片、樣式表、JS文件,所以這些文件將被複制到遍佈全球的CDN機器上。

靜態資源通常佔用了一個網站大部分的帶寬,通過CDN也很容易獲得。通常,網站會使用第三方提供者的CDN,而不是自己運作一個CDN。例如,Facebook的靜態文件是委託給最大的CDN提供者Akamai

作爲示範,當你ping static.ak.fbcdn.net時,你將得到一個從akamai.net服務器來的響應結果。有趣的是,如果你ping這個URL兩次,你可能會從不同的服務器得到響應結果,這也正是幕後負載平衡的作用的結果。

10.瀏覽器發送異步請求

異步請求

在web2.0時代,即使在頁面渲染後客戶端還是持續與服務器端通信。

例如,當你的朋友上線或下線時,Facebook聊天功能將會持續更新你已經登錄的朋友列表。爲了更新這個列表,你瀏覽器上運行的JS將會發送異步請求到服務器,異步請求是發送給特殊URL的GET或POST請求。在Facebook的例子中,客戶端會發送一個POST請求到http://www.facebook.com/ajax/chat/buddy_list.php,獲取你在線的朋友列表

這個模式被稱爲AJAX,是“Asynchronous JavaScript And XML”,的縮寫,雖然不太清楚爲什麼服務器必須將響應格式化爲xml。再舉個例子,在迴應異步請求時,Facebook會返回JS代碼片段。

除了其他的,fiddler這個工具能夠讓你看到瀏覽器發送的異步請求。事實上,你不是隻能被動的觀察這些請求,還能主動修改和重新發送它們。AJAX請求這麼容易被蒙,可着實讓那些計分的在線遊戲開發者們鬱悶的了。(當然,可別這樣騙人家)

Facebook聊天功能提供了一個很有意思的關於AJAX的問題案例:把數據從服務器端推送到客戶端。因爲HTTP是一個請求-響應協議,所以聊天服務器不能把新消息發給客戶。取而代之的是客戶端不得不隔幾秒就輪詢下服務器端看自己有沒有新消息。

在這些情況下,長輪詢是減輕服務器負載的有用技術。如果當被輪詢時服務器沒有新消息,它就不理這個客戶端請求。而當尚未超時的情況下收到了該客戶的新消息,服務器就會找到未完成的請求,把新消息做爲響應返回給客戶端。

總結

希望這篇文章給了你一些有關不同web模塊合作的更好思路。


到此原文就正式結束了,下面是我個人的一些補充和想法

總結一下上文十個步驟:

1. 輸入URL
2. 瀏覽器查詢域名IP地址
3. 瀏覽器發送HTTP請求到服務器
4. 服務器發送重定向到瀏覽器
5. 瀏覽器重新發送HTTP請求到重定向地址
6. 服務器處理HTTP請求
7. 服務器發送響應到瀏覽器
8. 瀏覽器根據響應結果渲染頁面
9. 瀏覽器發送請求獲取嵌入HTML頁面對象請求
10. 瀏覽器發送異步請求

擴展

一:什麼是DNS?

    DNS(Domain Name System,域名系統),因特網上作爲域名和IP地址相互映射的一個分佈式數據庫,能夠使用戶更方便的訪問互聯網,而不用去記住能夠被機器直接讀取的IP數串。通過主機名,最終得到該主機名對應的IP地址的過程叫做域名解析(或主機名解析)。

    通俗的講,我們更習慣於記住一個網站的名字,比如www.baidu.com,而不是記住它的ip地址,比如:167.23.10.2。而計算機更擅長記住網站的ip地址,而不是像www.baidu.com等鏈接。因爲,DNS就相當於一個電話本,比如你要找www.baidu.com這個域名,那我翻一翻我的電話本,我就知道,哦,它的電話(ip)是167.23.10.2。

二:DNS查詢的兩種方式:遞歸查詢和迭代查詢

1、遞歸解析

    當局部DNS服務器自己不能回答客戶機的DNS查詢時,它就需要向其他DNS服務器進行查詢。此時有兩種方式,如圖所示的是遞歸方式。局部DNS服務器自己負責向其他DNS服務器進行查詢,一般是先向該域名的根域服務器查詢,再由根域名服務器一級級向下查詢。最後得到的查詢結果返回給局部DNS服務器,再由局部DNS服務器返回給客戶端。

遞歸查詢

2、迭代解析

    當局部DNS服務器自己不能回答客戶機的DNS查詢時,也可以通過迭代查詢的方式進行解析,如圖所示。局部DNS服務器不是自己向其他DNS服務器進行查詢,而是把能解析該域名的其他DNS服務器的IP地址返回給客戶端DNS程序,客戶端DNS程序再繼續向這些DNS服務器進行查詢,直到得到查詢結果爲止。也就是說,迭代解析只是幫你找到相關的服務器而已,而不會幫你去查。比如說:baidu.com的服務器ip地址在192.168.4.5這裏,你自己去查吧,本人比較忙,只能幫你到這裏了。

迭代查詢

3、DNS域名稱空間的組織方式

    我們在前面有說到根DNS服務器,域DNS服務器,這些都是DNS域名稱空間的組織方式。按其功能命名空間中用來描述 DNS 域名稱的五個類別的介紹詳見下表中,以及與每個名稱類型的示例

域名結構

4、TCP三次握手

    第一次握手:客戶端A將標誌位SYN置爲1,隨機產生一個值爲seq=J(J的取值範圍爲=1234567)的數據包到服務器,客戶端A進入SYN_SENT狀態,等待服務端B確認;

    第二次握手:服務端B收到數據包後由標誌位SYN=1知道客戶端A請求建立連接,服務端B將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給客戶端A以確認連接請求,服務端B進入SYN_RCVD狀態。

    第三次握手:客戶端A收到確認後,檢查ack是否爲J+1,ACK是否爲1,如果正確則將標誌位ACK置爲1,ack=K+1,並將該數據包發送給服務端B,服務端B檢查ack是否爲K+1,ACK是否爲1,如果正確則連接建立成功,客戶端A和服務端B進入ESTABLISHED狀態,完成三次握手,隨後客戶端A與服務端B之間可以開始傳輸數據了。

三次握手

5、爲什需要三次握手?

  《計算機網絡》第四版中講“三次握手”的目的是“爲了防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤”

   書中的例子是這樣的,“已失效的連接請求報文段”的產生在這樣一種情況下:client發出的第一個連接請求報文段並沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以後的某個時間纔到達server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段後,就誤認爲是client再次發出的一個新的連接請求。於是就向client發出確認報文段,同意建立連接。

  假設不採用“三次握手”,那麼只要server發出確認,新的連接就建立了。由於現在client並沒有發出建立連接的請求,因此不會理睬server的確認,也不會向server發送數據。但server卻以爲新的運輸連接已經建立,並一直等待client發來數據。這樣,server的很多資源就白白浪費掉了。採用“三次握手”的辦法可以防止上述現象發生。例如剛纔那種情況,client不會向server的確認發出確認。server由於收不到確認,就知道client並沒有要求建立連接。”。主要目的防止server端一直等待,浪費資源。

6、TCP四次揮手

    第一次揮手:Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。

    第二次揮手:Server收到FIN後,發送一個ACK給Client,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。

    第三次揮手:Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。

    第四次揮手:Client收到FIN後,Client進入TIME_WAIT狀態,接着發送一個ACK給Server,確認序號爲收到序號+1,Server進入CLOSED狀態,完成四次揮手。

四次揮手

7、爲什麼建立連接是三次握手,而關閉連接卻是四次揮手呢?

    這是因爲服務端在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。而關閉連接時,當收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,己方也未必全部數據都發送給對方了,所以己方可以立即close,也可以發送一些數據給對方後,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送。

8、301和302的區別。

    301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器返回的這個狀態碼後會自動跳轉到一個新的URL地址,這個地址可以從響應的Location首部中獲取(用戶看到的效果就是他輸入的地址A瞬間變成了另一個地址B)——這是它們的共同點。

    他們的不同在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了),搜索引擎在抓取新內容的同時也將舊的網址換爲重定向之後的網址;

    302表示舊地址A的資源還在(仍然可以訪問),這個重定向只是臨時地從舊地址A跳轉到地址B,搜索引擎會抓取新的內容而保存舊的網址。 SEO 302好於301

9、重定向原因:

(1)網站調整(如改變網頁目錄結構);
(2)網頁被移到一個新地址;
(3)網頁擴展名改變(如應用需要把.php改成.Html或.shtml)。
    這種情況下,如果不做重定向,則用戶收藏夾或搜索引擎數據庫中舊地址只能讓訪問客戶得到一個404頁面錯誤信息,訪問流量白白喪失;再者某些註冊了多個域名的網站,也需要通過重定向讓訪問這些域名的用戶自動跳轉到主站點等。

10、什麼時候進行301或者302跳轉呢?

    當一個網站或者網頁24—48小時內臨時移動到一個新的位置,這時候就要進行302跳轉,而使用301跳轉的場景就是之前的網站因爲某種原因需要移除掉,然後要到新的地址訪問,是永久性的。
    清晰明確而言:使用301跳轉的大概場景如下:
    1、域名到期不想續費(或者發現了更適合網站的域名),想換個域名。
    2、在搜索引擎的搜索結果中出現了不帶www的域名,而帶www的域名卻沒有收錄,這個時候可以用301重定向來告訴搜索引擎我們目標的域名是哪一個。
    3、空間服務器不穩定,換空間的時候。

11、什麼是反向代理?

    客戶端本來可以直接通過HTTP協議訪問某網站應用服務器,網站管理員可以在中間加上一個Nginx,客戶端請求Nginx,Nginx請求應用服務器,然後將結果返回給客戶端,此時Nginx就是反向代理服務器。

反向代理

12、瀏覽器渲染過程

詳情可以看另一篇文章從web瀏覽器的渲染到性能優化

個人想法

    我個人覺得這是一個比較基礎但基本覆蓋面都涉及的解析文章,裏面內容詳略得當,其實很多點展開說又是一篇長文,另外每個步驟後面的擴展內容提及了很多有趣的內容,其實很多也是我們需要了解甚至深入研究的,比如CDN部署靜態資源,當然有些也是可以真正到了技術選型的時候在充分深入瞭解的。

    最後,估計原文已經寫了近兩年,web領域發展日新月異,所以原文可能會有一定侷限或缺漏在所難免,本文會持續維護,儘量詳細解析這個經典面試問題。


    最後給出一些本次翻譯參考的文獻,同時感謝谷歌和百度翻譯O(∩_∩)O~
  1. 當你在瀏覽器地址欄輸入一個URL後回車,將會發生的事情?(文章裏面有類似原文翻譯內容)

  2. 關於URL後加斜槓的說明

  3. 【原】老生常談-從輸入url到頁面展示到底發生了什麼

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