Google Chrome中的高效能網路

tech.uc.cn發表於2013-11-07

  Google Chrome的歷史和指導原則 【譯註】這部分不再詳細翻譯,只列出核心意思。

  驅動Chrome繼續前進的核心原則包括:

  Speed: 做最快的(fastest)的瀏覽器. Security:為使用者提供最為安全的(most secure)的上網環境。

  Stability: 提供一個健壯且穩定的(resilient and stable)的Web應用平臺。

  Simplicity: 以簡練的使用者體驗(simple user experience)封裝精益求精的技術(sophisticated technology)。

  本文關將注於第一點,速度。

  關於效能的方方面面 一個現代瀏覽器就是一個和作業系統一樣的平臺。在Chrome之前的瀏覽器都是單程式的應用,所有頁面共享相同的地址空間和資源。引入多程式架構這是Chrome最為著名的改進【譯註:省略一些反覆談論的細節】。

Google Chrome中的高效能網路_html_1c3ce373

  一個程式內,Web應用主要需要執行三個任務:獲取資源,頁面 排版及渲染,和執行JavaScript。渲染和指令碼都是在執行中交替以單執行緒的方式執行的,其原因是為了保持DOM的一致性,而JavaScript本 身也是一個單執行緒的語言。所以優化渲染和指令碼執行無論對於頁面開發者還是瀏覽器開發者都是極為重要的。

  Chrome的渲染引擎是WebKit, JavaScript Engine則使用深入優論的V8 (“V8″ JavaScript runtime)。但是,如果網路不暢,無論優化V8的JavaScript執行,還是優化WebKit的解析和渲染,作用其實很有限。巧婦難為無米之炊,資料沒來就得等著!

  相對於使用者體驗,作用最為明顯的就是如何優化網路資源的載入順 序、優先順序及每一個資源的延遲時間(latency)。也許你察覺不到,Chrome網路模組每天都在進步,逐步降低每個資源的載入成本:向DNS lookups學習,記住頁面拓撲結構(topology of the web), 預先連線可能的目標網址,等等,還有很多。從外面來看就是一個簡單的資源載入的機制,但在內部卻是一個精彩的世界。

  關於Web應用 開始正題前,還是先來了解一下現在網頁或者Web應用在網路上的需求。

  HTTP Archive 專案一直在追蹤網頁構建。除了頁面內容外,它還會分析流行頁面使用的資源數量,型別,頭資訊以及不同目標地址的後設資料(metadata)。下面是2013年1月的統計資料,由300,000目標頁面得出的平均資料:

Google Chrome中的高效能網路_html_m52ecc48a

  1280 KB 包含88個資源(Images,JavaScript,CSS …)

  連線15個以上的不同主機(distinct hosts)。

  這些數字在過去幾年中一直持續增長( steadily increasing ),沒有停下的跡象。這說明我們正不斷地建構一個更加龐大的、野心勃勃的網路應用。還要注意,平均來看每個資源不過12KB, 表明絕大多數的網路傳輸都是短促(short and bursty)的。這和TCP針對大資料、流式(streaming)下載的方向不一致,正因為如此,而引入了一些併發症。下面就用一個例子來抽絲剝繭,一窺究竟……

  一個Resource Request的一生 W3C的Navigation Timing specification定義了一組API,可以觀察到瀏覽器的每一個請求(request)的時序和效能資料。下面瞭解一些細節:

Google Chrome中的高效能網路_html_m69436d46

  給定一個網頁資源地址後,瀏覽器就會檢查本地快取和應用快取。如果之前獲取過並且有相應的快取資訊(appropriate cache headers)(如Expires, Cache-Control, etc.), 就會用快取資料填充這個請求,畢竟最快的請求就是沒有請求(the fastest request is a request not made)。否則,我們重新驗證資源,如果已經失效(expired),或者根本就沒見過,一個耗費網路的請求就無法避免地傳送了。

  給定了一個主機名和資源路徑後,Chrome先是檢查現有已建立的連線(existing open connections)是否可以複用, 即sockets指定了以(scheme、host和port)定義的連線池(pool)。但如果配置了一個代理,或者指定了 proxy auto-config (PAC)指令碼,Chrome就會檢查與proxy的連線。PAC指令碼基於URL提供不同的代理,或者為此指定了特定 的規則。與每一個代理間都可以有自己的socket pool。最後,上述情況都不存在,這個請求就會從DNS查詢(DNS lookup)開始了,以便獲得它的IP地址。

  幸運的話,這個主機名已經被快取過。否則,必須先發起一個 DNS Query。這個過程所需的時間和ISP,頁面的知名度,主機名在中間快取(intermediate caches)的可能性,以及authoritative servers的響應時間這些因素有關。也就是說這裡變數很多,不過一般還不致於到幾百毫秒那麼誇張。

Google Chrome中的高效能網路_html_15238f79

  拿到解析出的IP後,Chrome就會在目標地址間開啟一個新TCP連線,我們就要執行一個3度握手(“three-way handshake”): SYN > SYN-ACK > ACK。這個操作每個新的TCP連線都必須完成,沒有捷徑。根據遠近,路由路徑的選擇,這個過程可能要耗時幾百毫秒,甚至幾秒。而到現在,我們連一個有效的位元組都還沒收到。

  當TCP握手完成了,如果我們連線的是一個HTTPS地址,還有一個SSL握手過程,同時又要增加最多兩輪的延遲等待。如果SSL會話被快取了,就只需一次。

  最後,Chrome終於要傳送HTTP請求了 (如上面圖示中的requestStart)。 伺服器收到請求後,就會傳送響應資料(response data)回到客戶端。這裡包含最少的往返延遲和服務的處理時間。然後一個請求就完成了。但是,如果是一個HTTP重定向(redirect)的話?我們 又要從頭開始這個過程。如果你的頁面裡有些冗餘的重定向,最好三思一下!

  你得出所有的延遲時間了嗎? 我們假設一個典型的寬頻環境:沒有本地快取,相對較快的DNS lookup(50ms), TCP握手,SSL協商,以及一個較快伺服器響應時間(100ms)和一次延遲(80ms,在美國國內的平均值): 50ms for DNS 80ms for TCP handshake (one RTT)

  160ms for SSL handshake (two RTT’s)

  40ms (傳送請求到伺服器)

  100ms (伺服器處理)

  40ms (伺服器回傳響應資料)

  一個請求花了470毫秒, 其中80%的時間被網路延遲佔去了。看到了吧,我們真得有很多事情要做!事實上,470毫秒已經很樂觀了:

  如果伺服器沒有達到到初始TCP的擁塞視窗(congestion window),即4-15KB,就會引入更多的往返延遲。 SSL延遲也可能變得更糟。如果需要獲取一個沒有的認證(certificate)或者執行 online certificate status check (OCSP), 都會讓我們需要一個新的TCP連線,又增加了數百至上千毫秒的延遲。

  怎樣才算”夠快”? 前面可以看到伺服器響應時間僅是總延遲時間的20%,其它都被DNS,握手等操作佔用了。過去使用者體驗研究( user experience research )表明使用者對延遲時間的不同反應:

  延遲及使用者反應 0 – 100ms 迅速 100 – 300ms 有點慢 300 – 1000ms 機器還在執行 1s+ 想想別的事…… 10s+ 我一會再來看看吧…

  上表同樣適用於頁面的效能表現: 渲染頁面,至少要在250ms內給個迴應來吸引住使用者。這就是簡單地針對速度。從Google, Amazon, Microsoft,以及其它數千個站點來看,額外的延遲直接影響頁面表現:流暢的頁面會吸引更多的瀏覽、以及更強的使用者吸引力(engagement) 和頁面轉換率(conversion rates).

  現在我們知道了理想的延遲時間是250ms,而前面的示例告訴我們,DNS Lookup, TCP和SSL握手,以及request的準備時間花去了370ms, 即便不考慮伺服器處理時間,我們也超出了50%。

  對於絕大多數的使用者和網頁開發者來說,DNS, TCP,以及SSL延遲都是透明,很少有人會想到它。這也就是為什麼Chrome的網路模組那麼的複雜。

  我們已經識別出了問題,下面讓我們深入一下實現的細節…

相關文章