當你在瀏覽器位址列輸入一個URL後回車,將會發生的事情?
這道題目沒有所謂的完全的正確答案,這個題目可以讓你在任意的一個點深入下去, 只要你對這個點是熟悉的。以下是一個大概流程:
- 瀏覽器向DNS伺服器查詢輸入URL對應的IP地址。
- DNS伺服器返回網站的IP地址。
- 瀏覽器根據IP地址與目標web伺服器在80埠上建立TCP連線
- 瀏覽器獲取請求頁面的html程式碼。
- 瀏覽器在顯示視窗內渲染HTML。
- 視窗關閉時,瀏覽器終止與伺服器的連線。
這其中最有趣的是第1步和第2步(域名解析)。我們輸入的網址(域名)是IP地址的一個別名, 在一個DNS內,一個域名對應一個IP地址。域名系統(DNS) 的工作就是將域名與它的IP地址對應起來。DNS是分散式的,同時也是具有層級關係的。
一個域名伺服器雖然只記錄一個小的子網內的主機名和IP地址, 但所有的域名伺服器聯合起來工作,就能將全網內的域名與它們的IP地址對應起來。 這也就意味著,如果一個域名伺服器無法找到某個請求域名所對應的IP地址, 它就會向其它的域名伺服器發出請求進行尋找。
web前端效能:
即是web使用者在訪問一個頁面時所要花費的時間總和。即一個完全意義上的使用者響應時間,相對於伺服器的響應時間而言還會包括更多的內容和影響因素。那麼一個web頁面的完整請求包括了哪些部分的時間總和就是web前段效能分析和優化所需要了解的基礎知識,先了解一下使用者從瀏覽器訪問一個url後到頁面完全展示所有內容的整個過程吧。
頁面的請求過程:
7、瀏覽器接收返回的http內容
================================前端解析分割線===========================================
8、開始解析html檔案,當然是自上而下,先是頭部,後是body9、當解析到頭部css外部連結時,同步去下載,如果遇到外部js連結也是下載【不過js連結不建議放在頭部,因為耽誤頁面第一展現時間】
10、接著解析body部分,邊解析邊開始生成對應的DOM樹,同時等待css檔案下載
11、一旦css檔案下載完畢,那麼就同步去用已經生成的DOM節點+CSS去生成渲染樹
12、渲染樹一旦有結構模型了,接著就會同步去計算渲染樹節點的佈局位置
13、一旦計算出來渲染的座標後,又同步去開始渲染
14、10-13步進行過程中如果遇到圖片則跳過去渲染下面內容,等待圖片下載成功後會返回來在渲染原來圖片的位置
15、同14步,如果渲染過程中出現js程式碼調整DOM樹機構的情況,也會再次重新來過,從修改DOM那步開始
16、最終所有節點和資源都會渲染完成
=========================================分析結束分割線==============================================
17、渲染完成後開始page的onload事件18、整個頁面load完成整個過程中會有很多的分別請求,所以TCP連線會很多,並且每一個用完都會自己關了,除非是keep-live型別的可以請求多次才關閉。
第二章 WEB前臺的優化規則
一、儘量減少 HTTP 請求
有幾種常見的方法能切實減少 HTTP 請求:
1、 合併指令碼跟樣式檔案,如可以把多個 CSS 檔案合成一個,把多個 JS 檔案合成一個。
2、 CSS Sprites 利用 CSS background 相關元素進行背景圖絕對定位,把多個圖片合成一個圖片。
二、使用瀏覽器快取
在使用者瀏覽網站的不同頁面時,很多內容是重複的,比如相同的JS、CSS、圖片等。如果我們能夠建議甚至強制瀏覽器在本地快取這些檔案,將大大降低頁面產生的流量,從而降低頁面載入時間。
根據伺服器端的響應header,一個檔案對瀏覽器而言,有幾級不同的快取狀態。
1、伺服器端告訴瀏覽器不要快取此檔案,每次都到伺服器上更新檔案。
2、伺服器端沒有給瀏覽器任何指示。
3、在上次傳輸中,伺服器給瀏覽器傳送了Last-Modified或Etag資料,再次瀏覽時瀏覽器將提交這些資料到伺服器,驗證本地版本是否最新的,如果為最新的則伺服器返回304程式碼,告訴瀏覽器直接使用本地版本,否則下載新版本。一般來說,有且只有靜態檔案,伺服器端才會給出這些資料。
4、伺服器強制要求瀏覽器快取檔案,並設定了過期時間。在快取未到期之前,瀏覽器將直接使用本地快取檔案,不會與伺服器端產生任何通訊。
我們要做的是儘量強制瀏覽器到第四種狀態,特別是對於JS、CSS、圖片等變動較少的檔案。
三、使用壓縮元件
IE和Firefox瀏覽器都支援客戶端GZIP,傳輸之前,先使用GZIP壓縮再傳輸給客戶端,客戶端接收之後由瀏覽器解壓,這樣雖然稍微佔用了一些伺服器和客戶端的CPU,但是換來的是更高的頻寬利用率。對於純文字來講,壓縮率是相當可觀的。如果每個使用者節約50%的頻寬,那麼租用來的那點頻寬就可以服務多一倍的客戶,並且縮短了資料的傳輸時間。
四、圖片、JS的預載入
預載入影象最簡單的方法是在 JavaScript 中例項化一個新 Image() 物件,然後將需要載入的影象的 URL 作為引數傳入。
function preLoadImg(url) {
var img = new Image();
img.src = url;
}
可以在登入頁面預載入JS和圖片
五、將指令碼放在底部
指令碼放在頂部帶來的問題,
1、 使用指令碼時,對於位於指令碼以下的內容,逐步呈現將被阻塞
2、 在下載指令碼時會阻塞並行下載
放在底部可能會出現JS錯誤問題,當指令碼沒載入進來,使用者就觸發指令碼事件。
要綜合考慮情況。
六、將樣式檔案放在頁面頂部
如果樣式表任在載入,構建呈現樹就是一種浪費,樣式檔案放在頁面底部可能會出現兩種情況:
1、 白屏
2、 無樣式內容的閃爍
七、使用外部的JS和CSS
將內聯的JS和CSS做成外部的JS、CSS。減少重複下載內聯的JS和CSS。
八、切分元件到多個域
主要的目的是提高頁面元件並行下載能力。但不要跨太多域名,建議採用2個子域名。
九、精簡JS
可以做到兩個級別
1、精簡:從程式碼中移除不必要的字元以減少其大小,
2、混淆:在精簡的同時,還會改寫程式碼,函式、變數名被轉換成更短的字串
十、精簡CSS
從程式碼中移除不必要的字元以減少其大小,
十一、 精簡圖片、Flash
對大圖片、Flash,要在效果和大小之間做出平衡。
第三章 程式的優化
第四章 資料庫的優化
附錄A 頁面請求分析
從輸入URL到頁面呈現需要下面5個步驟
1. 輸入URL地址或者點選URL的一個連結
2. 瀏覽器根據URL地址,結合DNS,解析出URL對應的IP地址
3. 傳送HTTP請求
4. 開始連線請求的伺服器並且請求相關的內容
5. 瀏覽器解析從伺服器端返回的內容,並且把頁面顯現出來
上面基本上就是一個頁面從請求到實現的基本過程,下面我們將剖析這個過程。
當輸入URL之後,瀏覽器就要知道這個URL對應的IP是什麼,只有知道了IP地址,瀏覽器才能準備的把請求傳送到指定的伺服器的具體IP和埠號上面。瀏覽器的DNS解析器負責把URL解析為正確的IP地址。這個解析的工作是要花時間的,而且這個解析的時間段內,瀏覽器不是能從伺服器那裡下載到任何的東西的。瀏覽器和操縱系統提供了DNS解析快取支援。
當獲得了IP地址之後,那麼瀏覽器就向伺服器傳送HTTP的請求,過程如下:
1.瀏覽器通過傳送一個TCP的包,要求伺服器開啟連線
2.伺服器也通過傳送一個包來應答客戶端的瀏覽器,告訴瀏覽器連線開了。
3.瀏覽器傳送一個HTTP的GET請求,這個請求包含了很多的東西了,例如我們常見的cookie和其他的head頭資訊。
這樣,一個請求就算是發過去了。
請求傳送去之後,之後就是伺服器的事情了,伺服器端的程式把最後的結果傳送到客戶端。
其實首先到達瀏覽器的就是html的那些文件,所謂的html的文件,就是純粹的html程式碼,不包含什麼圖片,指令碼,CSS等的。也就是頁面的html結構。因為此時返回的只是頁面的html結構。這個html文件的傳送到瀏覽器的時間是很短的,一般是佔整個響應時間的10%左右。
這樣之後,那麼頁面的基本的骨架就在瀏覽器中了,下一步就是瀏覽器解析頁面的過程,也就是一步步從上到下的解析html的骨架了。
如果此時在html文件中,遇到了img標籤,那麼瀏覽器就會發送HTTP請求到這個img響應的URL地址去獲取圖片,然後呈現出來。如果在html文件中有很多的圖片,flash,那麼瀏覽器就會一個個的請求,然後呈現,如果每個圖片都要請求,那麼就要進行之前說的那些步驟:解析url,開啟tcp連線等等。開啟連線也是要消耗資源的,就像我們在進行資料庫訪問一樣,我們也是儘可能的少開資料庫連線,多用連線池中的連線。道理一樣,tcp連線也是可以重用的。http1.1提出了持久連線(persistent connection)的概念,也就是說同一條 HTTP 連線,可以同時處理多個請求,減少tcp連線。
當頁面的html骨架載入了之後,瀏覽器就開始解析頁面中標籤,從上到下開始解析。
首先是head標籤的解析,如果發現在head中有要引用的JS指令碼,那麼瀏覽器此時就開始請求指令碼,此時整個頁面的解析過程就停了下來,一直到JS請求完畢。之後頁面接著向下解析,如解析body標籤,如果在body中有img標籤,那麼瀏覽器就會請求img的src對應的資源,如果有多個img標籤,那麼瀏覽器就一個個的解析,解析不會像JS那樣等待的,會併發的下載。
綜上所述:一個頁面的請求等於一個或多個url的請求,因此一個頁面裡包含的外部請求數會影響頁面的整體效能【每請求一次就要多佔用一次cpu使用、多一次tcp連線】每個url的請求又包括定址、連線、請求傳輸、返回傳輸、斷連的過程;因此每個階段的外部環境也會影響整體效能【DNS伺服器的定址時間,請求和返回內容時的網路環境】除了URL請求數量外,每個請求的內容大小也是影響效能的主要因素【檔案越大消耗在傳輸過程中的時間就越長】請求同樣多的資源,並行請求和序列請求速率是不一樣的,所以請求的資源要儘量支援同步請求【同步請求不同資源,即請求被髮送到不同的資源伺服器即可】依據瀏覽器的載入、渲染機制,選擇合適的HTML內容排版方式【減少反覆建立物件例項的次數、充分利用快取機制】優先載入使用者關注的內容【css載入優於js內容,首屏內容優於非首屏內容】關注完http請求的過程後,再來關注整個請求過程中關注的幾個時間點,通過確定時間點就可以確定影響效能的時間段,就是確定影響效能的因素。根據上面的介紹主要的幾個時間點又可以分頁面的整體時間點、以及單個url請求過程中的時間點。【基於httpanalyzer工具的指標】
單個url請求的主要時間點:1、Cache Read:快取讀取時間,或304錯誤的處理時間 2、Block:請求等待時間,取決於快取檢查,網路連線等待3、DNS Lookup:DNS伺服器查詢時間,取決於dns服務的數量,dns註冊的域4、Connect:tcp連線的總時間,取決於連線型別,ssh,keepalive都會比http長5、Send first to last:傳送請求內容的時間,取決於請求內容大小,及上行的傳輸速度6、Wait:等待響應的時間,取決於網路環境的響應,web伺服器的處理時間7、Receive first to last:接收響應內容的時間,取決於響應內容,下行的傳輸速度,也要考慮伺服器的頻寬8、Time to first byte:從請求一直到接收到第一個字元的總時間,等於1+2+3+4+5+69、Network:網路消耗時間,等於3+410、Begin to end:整個請求的總時間,等於1+2+3+4+5+6+7單個頁面的主要時間點:1、DOM Ready Time: DOM完成的時間,從接收html到完全轉換成dom樹所需的時間2、DOM Ready to Page Load: 頁面元素的載入和渲染完成時間,包括html,css,img及其它內容3、Page Load Time: page頁onload事件的時間,其實際時間等於總時間 - (DOM ready + 元素渲染時間)4、URL Requests Begin to End:url請求所消耗的所有時間,從傳送請求發起到接收最後一個位元組斷開5、Network Time:消耗在網路上的時間,即tcp的連線時間6、Begin to End:所有消耗的時間,包括請求結束後的渲染時間1.瀏覽器獲得url對應的請求,向作業系統請求該url對應的iP地址
2.作業系統查詢DNS (首先查詢本地host檔案,沒有則查詢網路)獲得對應ip地址
3.瀏覽器傳送tcp連線請求向 ip地址對應的伺服器(帶SYN標誌資料包)。
4.伺服器收到tcp連線請求後,回覆可以連結請求(有SYN/ACK標誌的資料包)。
5.瀏覽器收到回傳的資料,確認ok後,還會向服務端傳送資料(帶ACK標誌的資料包)包表示三次握手結束。
6.三次握手成功後,瀏覽器和服務端開始tcp連線形式傳輸資料包。
7.伺服器傳給瀏覽所需要的資源資料。
8.瀏覽器獲得資料,渲染網頁然後呈現給使用者。
作為一個軟體開發者,你一定會對網路應用如何工作有一個完整的層次化的認知,同樣這裡也包括這些應用所用到的技術:像瀏覽器,HTTP,HTML,網路伺服器,需求處理等等。
本文將更深入的研究當你輸入一個網址的時候,後臺到底發生了一件件什麼樣的事~
1. 首先嘛,你得在瀏覽器裡輸入要網址:
2. 瀏覽器查詢域名的IP地址
導航的第一步是通過訪問的域名找出其IP地址。DNS查詢過程如下:
- 瀏覽器快取 – 瀏覽器會快取DNS記錄一段時間。 有趣的是,作業系統沒有告訴瀏覽器儲存DNS記錄的時間,這樣不同瀏覽器會儲存個自固定的一個時間(2分鐘到30分鐘不等)。
- 系統快取 – 如果在瀏覽器快取裡沒有找到需要的記錄,瀏覽器會做一個系統呼叫(windows裡是gethostbyname)。這樣便可獲得系統快取中的記錄。
- 路由器快取 – 接著,前面的查詢請求發向路由器,它一般會有自己的DNS快取。
- ISP DNS 快取 – 接下來要check的就是ISP快取DNS的伺服器。在這一般都能找到相應的快取記錄。
- 遞迴搜尋 – 你的ISP的DNS伺服器從跟域名伺服器開始進行遞迴搜尋,從.com頂級域名伺服器到Facebook的域名伺服器。一般DNS伺服器的快取中會有.com域名伺服器中的域名,所以到頂級伺服器的匹配過程不是那麼必要了。
DNS遞迴查詢如下圖所示:
DNS有一點令人擔憂,這就是像wikipedia.org 或者 facebook.com這樣的整個域名看上去只是對應一個單獨的IP地址。還好,有幾種方法可以消除這個瓶頸:
- 迴圈 DNS 是DNS查詢時返回多個IP時的解決方案。舉例來說,Facebook.com實際上就對應了四個IP地址。
- 負載平衡器 是以一個特定IP地址進行偵聽並將網路請求轉發到叢集伺服器上的硬體裝置。 一些大型的站點一般都會使用這種昂貴的高效能負載平衡器。
- 地理 DNS 根據使用者所處的地理位置,通過把域名對映到多個不同的IP地址提高可擴充套件性。這樣不同的伺服器不能夠更新同步狀態,但對映靜態內容的話非常好。
- Anycast 是一個IP地址對映多個物理主機的路由技術。 美中不足,Anycast與TCP協議適應的不是很好,所以很少應用在那些方案中。
大多數DNS伺服器使用Anycast來獲得高效低延遲的DNS查詢。
3. 瀏覽器給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[...]
GET 這個請求定義了要讀取的URL: “http://facebook.com/”。 瀏覽器自身定義 (User-Agent 頭), 和它希望接受什麼型別的相應 (Accept andAccept-Encoding 頭). Connection頭要求伺服器為了後邊的請求不要關閉TCP連線。
請求中也包含瀏覽器儲存的該域名的cookies。可能你已經知道,在不同頁面請求當中,cookies是與跟蹤一個網站狀態相匹配的鍵值。這樣cookies會儲存登入使用者名稱,伺服器分配的密碼和一些使用者設定等。Cookies會以文字文件形式儲存在客戶機裡,每次請求時傳送給伺服器。
用來看原始HTTP請求及其相應的工具很多。作者比較喜歡使用fiddler,當然也有像FireBug這樣其他的工具。這些軟體在網站優化時會幫上很大忙。
除了獲取請求,還有一種是傳送請求,它常在提交表單用到。傳送請求通過URL傳遞其引數(e.g.: http://robozzle.com/puzzle.aspx?id=85)。傳送請求在請求正文頭之後傳送其引數。像“http://facebook.com/”中的斜槓是至關重要的。這種情況下,瀏覽器能安全的新增斜槓。而像“http: //example.com/folderOrFile”這樣的地址,因為瀏覽器不清楚folderOrFile到底是資料夾還是檔案,所以不能自動新增 斜槓。這時,瀏覽器就不加斜槓直接訪問地址,伺服器會響應一個重定向,結果造成一次不必要的握手。
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/”。
為什麼伺服器一定要重定向而不是直接發會使用者想看的網頁內容呢?這個問題有好多有意思的答案。
其中一個原因跟搜尋引擎排名有 關。你看,如果一個頁面有兩個地址,就像http://www.igoro.com/ 和http://igoro.com/,搜尋引擎會認為它們是兩個網站,結果造成每一個的搜尋連結都減少從而降低排名。而搜尋引擎知道301永久重定向是 什麼意思,這樣就會把訪問帶www的和不帶www的地址歸到同一個網站排名下。
還有一個是用不同的地址會造成快取友好性變差。當一個頁面有好幾個名字時,它可能會在快取裡出現好幾次。
5. 瀏覽器跟蹤重定向地址
現在,瀏覽器知道了“http://www.facebook.com/”才是要訪問的正確地址,所以它會發送另一個獲取請求:
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. 伺服器“處理”請求
伺服器接收到獲取請求,然後處理並返回一個響應。
這表面上看起來是一個順向的任務,但其實這中間發生了很多有意思的東西- 就像作者部落格這樣簡單的網站,何況像facebook那樣訪問量大的網站呢!
- Web 伺服器軟體web伺服器軟體(像IIS和阿帕奇)接收到HTTP請求,然後確定執行什麼請求處理來處理它。請求處理就是一個能夠讀懂請求並且能生成HTML來進行響應的程式(像ASP.NET,PHP,RUBY...)。
舉 個最簡單的例子,需求處理可以以對映網站地址結構的檔案層次儲存。像http://example.com/folder1/page1.aspx這個地 址會對映/httpdocs/folder1/page1.aspx這個檔案。web伺服器軟體可以設定成為地址人工的對應請求處理,這樣 page1.aspx的釋出地址就可以是http://example.com/folder1/page1。
- 請求處理請求處理閱讀請求及它的引數和cookies。它會讀取也可能更新一些資料,並講資料儲存在伺服器上。然後,需求處理會生成一個HTML響應。
所 有動態網站都面臨一個有意思的難點 -如何儲存資料。小網站一半都會有一個SQL資料庫來儲存資料,儲存大量資料和/或訪問量大的網站不得不找一些辦法把資料庫分配到多臺機器上。解決方案 有:sharding (基於主鍵值講資料表分散到多個數據庫中),複製,利用弱語義一致性的簡化資料庫。
委 託工作給批處理是一個廉價保持資料更新的技術。舉例來講,Fackbook得及時更新新聞feed,但資料支援下的“你可能認識的人”功能只需要每晚更新 (作者猜測是這樣的,改功能如何完善不得而知)。批處理作業更新會導致一些不太重要的資料陳舊,但能使資料更新耕作更快更簡潔。
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 [email protected][...]
整個響應大小為35kB,其中大部分在整理後以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要去設定(前面這個響應裡沒有這點)和隱私資訊等等。
請注意報頭中把Content-type設定為“text/html”。報頭讓瀏覽器將該響應內容以HTML形式呈現,而不是以檔案形式下載它。瀏覽器會根據報頭資訊決定如何解釋該響應,不過同時也會考慮像URL擴充套件內容等其他因素。
8. 瀏覽器開始顯示HTML
在瀏覽器沒有完整接受全部HTML文件時,它就已經開始顯示這個頁面了:
9. 瀏覽器傳送獲取嵌入在HTML中的物件
在瀏覽器顯示HTML時,它會注意到需要獲取其他地址內容的標籤。這時,瀏覽器會發送一個獲取請求來重新獲得這些檔案。
下面是幾個我們訪問facebook.com時需要重獲取的幾個URL:
- 圖片http://static.ak.fbcdn.net/rsrc.php/z12E0/hash/8q2anwu7.gifhttp://static.ak.fbcdn.net/rsrc.php/zBS5C/hash/7hwy7at6.gif…
- CSS 式樣表http://static.ak.fbcdn.net/rsrc.php/z448Z/hash/2plh8s4n.csshttp://static.ak.fbcdn.net/rsrc.php/zANE1/hash/cvtutcee.css…
- JavaScript 檔案http://static.ak.fbcdn.net/rsrc.php/zEMOA/hash/c8yzb6ub.jshttp://static.ak.fbcdn.net/rsrc.php/z6R9L/hash/cq2lgbs8.js…
這些地址都要經歷一個和HTML讀取類似的過程。所以瀏覽器會在DNS中查詢這些域名,傳送請求,重定向等等...
但 不像動態頁面那樣,靜態檔案會允許瀏覽器對其進行快取。有的檔案可能會不需要與伺服器通訊,而從快取中直接讀取。伺服器的響應中包含了靜態檔案儲存的期限 資訊,所以瀏覽器知道要把它們快取多長時間。還有,每個響應都可能包含像版本號一樣工作的ETag頭(被請求變數的實體值),如果瀏覽器觀察到檔案的版本 ETag資訊已經存在,就馬上停止這個檔案的傳輸。
試著猜猜看“fbcdn.net”在地址中代表什麼?聰明的答案是"Facebook內容分發網路"。Facebook利用內容分發網路(CDN)分發像圖片,CSS表和JavaScript檔案這些靜態檔案。所以,這些檔案會在全球很多CDN的資料中心中留下備份。
靜態內容往往代表站點的頻寬大小,也能通過CDN輕鬆的複製。通常網站會使用第三方的CDN。例如,Facebook的靜態檔案由最大的CDN提供商Akamai來託管。
舉例來講,當你試著ping static.ak.fbcdn.net的時候,可能會從某個akamai.net伺服器上獲得響應。有意思的是,當你同樣再ping一次的時候,響應的伺服器可能就不一樣,這說明幕後的負載平衡開始起作用了。
10. 瀏覽器傳送非同步(AJAX)請求
在Web 2.0偉大精神的指引下,頁面顯示完成後客戶端仍與伺服器端保持著聯絡。
以 Facebook聊天功能為例,它會持續與伺服器保持聯絡來及時更新你那些亮亮灰灰的好友狀態。為了更新這些頭像亮著的好友狀態,在瀏覽器中執行的 JavaScript程式碼會給伺服器傳送非同步請求。這個非同步請求傳送給特定的地址,它是一個按照程式構造的獲取或傳送請求。還是在Facebook這個例 子中,客戶端傳送給http://www.facebook.com/ajax/chat/buddy_list.php一個釋出請求來獲取你好友裡哪個 線上的狀態資訊。
提起這個模式,就必須要講講"AJAX"-- “非同步JavaScript 和 XML”,雖然伺服器為什麼用XML格式來進行響應也沒有個一清二白的原因。再舉個例子吧,對於非同步請求,Facebook會返回一些JavaScript的程式碼片段。
除了其他,fiddler這個工具能夠讓你看到瀏覽器傳送的非同步請求。事實上,你不僅可以被動的做為這些請求的看客,還能主動出擊修改和重新發送它們。AJAX請求這麼容易被蒙,可著實讓那些計分的線上遊戲開發者們鬱悶的了。(當然,可別那樣騙人家~)
Facebook聊天功能提供了關於AJAX一個有意思的問題案例:把資料從伺服器端推送到客戶端。因為HTTP是一個請求-響應協議,所以聊天伺服器不能把新訊息發給客戶。取而代之的是客戶端不得不隔幾秒就輪詢下伺服器端看自己有沒有新訊息。
這些情況發生時長輪詢是個減輕伺服器負載挺有趣的技術。如果當被輪詢時伺服器沒有新訊息,它就不理這個客戶端。而當尚未超時的情況下收到了該客戶的新訊息,伺服器就會找到未完成的請求,把新訊息做為響應返回給客戶端。
總結一下
希望看了本文,你能明白不同的網路模組是如何協同工作的
在瀏覽器裡輸入網址或者點選連結,網頁打開了……這是我們上網時再普通不過的一幕,但是如此簡單的表象背後,卻隱藏著無比複雜的技術流程。想漲漲知識嗎?往下看吧。
一個HTTP請求的過程
為了簡化我們先從一個HTTP請求開始,簡要介紹一下一個HTTP求情的網路傳輸過程,也就是所謂的“從輸入URL到頁面下載完的過程中都發生了什麼事情”。
● DNS Lookup 先獲得URL對應的IP地址
● Socket Connect 瀏覽器和伺服器建立TCP連線
● Send Request 傳送HTTP請求
● Content Download 伺服器傳送響應
如果下到物理層去講就有點耍流氓了。如果這些你還認可這幾個步驟的話,我們就來講一下這裡面存在的效能問題。
● 如果你對DNS的查詢還有印象的話現在反思一下,DNS Lookup就是為了獲取一串IP地址要和無數個DNS伺服器進行通訊,這要消耗多少時間?別忘了,你查詢完了的時候,你還沒和那邊的伺服器通訊呢。
● TCP連線要三次握手。如果伺服器很遠的話這三次握手要花多少時間?別忘了建立連線之後你還沒發請求呢。(通常到這裡0.5秒就出去了)
● 傳送HTTP請求的時候你要知道一點,就是我們的網路頻寬上行和下行通常是不一樣的,通常上行的頻寬會小一些,一個的話還好,但是現在的網頁通常都會後續請求很多資源,頻寬小的時候上行擁塞怎麼辦?別忘了已經到第三步了,伺服器還沒給你發響應呢,現在你的瀏覽器還什麼都畫不出來。
● 終於到了伺服器發響應了,不巧你訪問的這個伺服器比較忙,好幾萬個人都要這個資源,伺服器的上行頻寬也是有限的,怎麼辦?
我覺得我出了幾道還不錯的面試題。順便提一下,前兩步的延遲和網路頻寬的影響不大;後兩步加頻寬是能一定程度緩解,不過你要有錢,而且很貴。
雖說博主做過WebKit本地渲染的優化,但是深知網頁載入的主要時間還是浪費在網路通訊上,所以在這些步驟上的優化會比你在瀏覽器核心的優化省力且效果明顯。
網路方面的主要優化手段,總結一下不外乎快取、預取、壓縮、並行。以後如果再有面試問效能優化之類的問題,大家都可以照著這個思路去考慮。
下面就分階段介紹一下現有的優化手段。
DNS優化
對於DNS優化,快取無疑是最簡單粗暴且效果明顯的了。說到快取就一定要提到快取層級:
● 瀏覽器DNS快取
● 系統DNS快取
● Hosts檔案
● 各個DNS伺服器上的快取
當然DNS快取失效期通常都比較短,很多情況下都要再去查詢。為了降低使用者體驗到的延遲(注意這裡不是網路延時),預取是一個不錯的方法。
比如說你敲網址的時候還沒有敲完,但是瀏覽器根據你的歷史發現你很有可能去訪問哪個網站,就提前給你做DNS預取了,比如你打了一個“w”的時候,chrome已經幫你去找weibo.com的IP地址了。chrome使用者看一下chrome://predictors 你就知道了。
此外瀏覽器還會記錄你過去的歷史,知道每個域名下通常還會有哪些其他的連結,以便建立起網站的拓撲結構。當你訪問這個域名下的網站,它就會預先對其他連結的域名進行DNS解析。
TCP優化
看到前面的DNS的具體優化這麼繁雜,知道這簡單的一步沒那麼簡單了吧。
結果到TCP這一步優化反而簡單了,因為剛才DNS已經把IP都預先弄到了,那麼我們順著剛才的步驟再建立連線就好了。
所以在你敲第一個字母的時候,DNS解析完了就去建立連線了,這時候你可能網址還沒敲完。當你剛訪問一個網站的時候,瀏覽器刷刷刷的幫你把到別的伺服器的TCP連線給你建好。
HTTP傳輸優化
寫到這裡可能有人會想,既然已經把TCP連線建立好了,那我乾脆預取更進一步,把所有的連結內容直接預取下來不就好了,這樣我網址還沒敲完網頁就已經載入完成了。
這個想法是好的,但現實卻是殘酷的,因為要記住我們的頻寬是有限的,DNS和TCP連線量級都比較輕,對網路頻寬不會佔據太多,但是HTTP傳輸就不一樣了。如果你所有連結都去預取的話,你的頻寬很快就被佔滿了,這樣你正常的請求無法得到滿足,效能反而會嚴重下降。
快取就又出現了,提快取必提層次結構。
● PageCache 這個是最快的了,直接在記憶體中快取了現有網頁的DOM結構和渲染結果,這就是你為什麼在點前進後退的時候會這麼快。
● HTTP Cache 檔案級別的Cache存在本地的檔案系統上按照RFC2616實現。
● 代理Cache 如果是通過代理伺服器上網的話,代理伺服器通常也會按照快取標準
● CDN 一個地理上離你很近的內容伺服器,比如說你在北京請求杭州淘寶的一個圖片,結果在北京的一個CDN上有這個圖片,那麼就不用去杭州了。
● DMOC(distributed memory object caching system)CDN主要存放的是靜態資料,但是網頁中通常有很多動態的資料需要查資料庫,流量多了壓力就會很大,通常伺服器外圍還會有一層記憶體快取伺服器,專門快取這些資料庫中的物件,據《淘寶技術這10年》稱可以減少99.5%的資料庫訪問。
● Server 其實真正落在伺服器上的請求已經不多了。
大家看到這裡有沒有想到能在什麼地方再加一層快取呢?其實可以在2和3之間加,也就是在路由器上加快取。
小米路由器和搜狗合作的預取引擎其實就相當於在路由器上加一層快取款順便智慧預取一下。為什麼在這裡另起一段專門談小米呢?難不成是小米的水軍?才不是呢,是因為博主看到這個訊息的時候心都涼了,和博主的畢設撞車了有木有。
去年在360剛出隨身Wi-Fi的時候博主想到了這麼個點子,還想著把這個東西做出來之後用這個創業和360談合作,結果最近剛做完,論文也投出去了,幻想著開啟人生巔峰,顛覆行業,結果就發現小米和搜狗出了這麼個一樣的東西還都商業化了。說好的人生巔峰就這樣沒有了,早知道去年就先申請個專利了。
另一個HTTP常用的優化就是壓縮了,網路傳輸時間=訊息大小/網速。既然網速比較貴那麼就壓縮一下吧,大部分伺服器都會對HTTP訊息進行gzip壓縮。可以在Http Header中看到,具體的就不細說了。
未來協議:SPDY
上面的都是傳統做法,下面講一個未來的技術。由於HTTP協議是上個世紀制定的協議了,已經不能很好地適應現在Web的發展,所以Google提出了SPDY協議,目前是指定中的HTTP2.0標準的一個底版。
SPDY主要有下面的特點:
● 一個TCP連線上並行多個HTTP連線,減少連線的建立時間。
● 請求優先順序(目前還沒看到具體實現)。
● HTTP頭部壓縮,上文提到的HTTP壓縮是對HTTP body的壓縮,並沒有對頭部壓縮。對於小的HTTP訊息,頭部的比重還是很大的,而現在的web中存在大量小訊息。
● Server push/hint 伺服器主動推送物件(可以想象成伺服器幫客戶端預取)。
業界目前對SPDY是有贊有彈,博主也持謹慎的態度,主要在1和4上,4其實和之前提到的HTTP直接預取的矛盾點一樣,萬一推送的不需要又佔據了頻寬怎麼辦?hint到底該如何實現都有困難。
第一條潛在的風險就是TCP連線中途斷開,那麼所有的連線就全部停掉了,PC網際網路這種情況可能會少一些,但是移動網際網路中TCP連線斷開的情況還是比較常見的。
不過作為一個未來的技術,還是有必要關注一下。