為什么谷歌瀏覽器比其它瀏覽器好用?
Google Chrome的歷史和指導(dǎo)性原則
Google Chrome最初是2008年下半年作為Windows平臺上的一個beta版本發(fā)布的。Google還將自己編寫的Chrome在BSD許可下進(jìn)行了開源——稱為Chromium。在很多人看來,這一串事件的發(fā)生頗為意外:瀏覽器戰(zhàn)爭要再次重啟了嗎?Google真的能做的更好嗎?
“它非常優(yōu)秀讓我不得不改變主意……”。埃里克•施密特在談到他一開始反對開發(fā)Google Chrome時這樣說道。
答案是,他們能。時至今日,Chrome已經(jīng)成為使用最廣泛的網(wǎng)絡(luò)瀏覽器(根據(jù)StatCounter的數(shù)據(jù),市場份額超過35%),并且兼容Windows、Linux、OS X、Chrome OS多種操作系統(tǒng),還包括Android和iOS等移動平臺。顯然,Chrome的特性和功能很對用戶的胃口,其很多創(chuàng)新之舉還被其他流行的瀏覽器所吸收和學(xué)習(xí)。
有一本解釋Google Chrome的思想和創(chuàng)新的原版38頁漫畫書,它很好地概括了Chrome受歡迎背后的思路和設(shè)計過程。但這只是開始。最初推動著Chrome開發(fā)的那些核心原則仍然是它持續(xù)優(yōu)化的指導(dǎo)性原則:
快速
做出最快的瀏覽器
安全
為用戶提供最安全的環(huán)境
穩(wěn)定
提供有彈性且穩(wěn)定的網(wǎng)絡(luò)應(yīng)用平臺
簡單
技術(shù)精妙蘊含在簡單的用戶體驗中
正如開發(fā)團隊所看到的那樣,我們今天所使用的很多網(wǎng)站都不再僅僅是網(wǎng)頁,而是應(yīng)用程序。這些越來越有野心的應(yīng)用需要速度、安全和穩(wěn)定。這些方面,每個都值得單獨成文來介紹,不過,因為我們的主題是性能,我們將重點介紹性能。
性能的多個方面
現(xiàn)代瀏覽器是一個平臺,就像你的操作系統(tǒng)一樣,Google Chrome也遵循這樣的設(shè)計理念。在Google Chrome之前,所有主流瀏覽器都是單進(jìn)程的應(yīng)用程序。所有打開的頁面共享同一地址空間,爭奪同一資源。任何頁面或瀏覽器本身出現(xiàn)bug,整體體驗都可能被破壞。
與此相反,Chrome運行于多進(jìn)程模型,這種模型可以保持進(jìn)程和內(nèi)存的隔離性,每個標(biāo)簽頁都能擁有一個嚴(yán)密的安全沙盒。隨著多核架構(gòu)的流行,隔絕進(jìn)程并使各個打開的標(biāo)簽頁免受其他出錯頁面的影響,單是這一點就能證明Chrome在瀏覽器的競爭中具有顯著的性能優(yōu)勢。實際上,我們會發(fā)現(xiàn)絕大多數(shù)其他瀏覽器都紛紛效仿Chrome,或者開始轉(zhuǎn)向類似的架構(gòu)。
分派進(jìn)程之后,一個Web程序的執(zhí)行主要包括三項任務(wù):獲取資源,頁面布局與呈現(xiàn),以及執(zhí)行JavaScript。呈現(xiàn)和腳本執(zhí)行步驟遵循單線程、交錯執(zhí)行的模型——無法對所得到的DOM(文檔對象模型)進(jìn)行并發(fā)式的修改。原因之一是JavaScript本身就是單線程的語言。所以,無論是對于應(yīng)用程序的開發(fā)者還是瀏覽器的開發(fā)者來說,優(yōu)化呈現(xiàn)和腳本執(zhí)行運行時的協(xié)作方式,是極其重要的。
在呈現(xiàn)這一步,Chrome使用的是Blink,這是一種快速、開源、遵守良好標(biāo)準(zhǔn)的布局引擎。在JavaScript這一步,Chrome自帶了一個高度優(yōu)化的V8 JavaScript運行時,它也作為單獨開源項目發(fā)布,并已經(jīng)被其他很多流行的項目所吸納,例如Node.js的運行時。但是如果瀏覽器的網(wǎng)絡(luò)連接是阻塞的(等待資源到來),那么優(yōu)化V8 JavaScript執(zhí)行或者Blink解析和呈現(xiàn)管道都不會有太大作用。
瀏覽器優(yōu)化各項網(wǎng)絡(luò)資源的次序、優(yōu)先級和延遲的能力對整體用戶體驗是最關(guān)鍵的影響因素。你可能沒有注意到,毫不夸張地說,Chrome的網(wǎng)絡(luò)棧每天都會變得更加聰明,嘗試著隱藏或減少各項資源的延遲開銷:它會學(xué)習(xí)可能出現(xiàn)的DNS查詢,會記住網(wǎng)絡(luò)的拓?fù)浣Y(jié)構(gòu),會預(yù)連接可能的目標(biāo)站點,等等。從外部看來,它展示出的是一種簡單的資源獲取機制,但是從內(nèi)部看來則是對如何優(yōu)化網(wǎng)絡(luò)性能并帶給用戶最佳體驗的一次詳細(xì)而精彩的案例學(xué)習(xí)。
讓我們進(jìn)入正題吧。
什么是現(xiàn)代Web應(yīng)用?
在我們接觸如何優(yōu)化網(wǎng)絡(luò)交互的具體細(xì)節(jié)之前,先來了解我們所面對的這個問題的發(fā)展趨勢和背景。換句話說就是,“現(xiàn)代網(wǎng)頁或者應(yīng)用是什么樣的?”
HTTP Archive項目記錄了網(wǎng)絡(luò)的構(gòu)造過程,可以幫助我們回答這個問題。這個項目并不是為了爬取網(wǎng)頁內(nèi)容,而是周期性地爬取訪問量最大的站點,記錄和加總各個站點所用資源數(shù)量、內(nèi)容類型、標(biāo)頭和其他元數(shù)據(jù)的分析數(shù)據(jù)。2013年1月的數(shù)據(jù),可能會令你吃驚。訪問量最大前30萬個網(wǎng)絡(luò)站點來看,一個頁面平均:
為1280KB大小
由88個資源組成
連接15個以上不同的主機
好好琢磨一下。大小平均超過1MB,包含88個如圖片、JavaScript、CSS這樣的資源,從15個不同的自有和第三方主機傳送出來。而且,這些數(shù)字在過去幾年還在持續(xù)增長,絲毫沒有減緩的跡象。這說明,我們所開發(fā)的網(wǎng)絡(luò)應(yīng)用正變得越來越大,越來越有野心。
根據(jù)HTTP Archive的數(shù)據(jù),做個簡單的算術(shù),我們可知每個資源平均大小為15KB(1280KB/88項資源),這意味著瀏覽器中大多數(shù)的網(wǎng)絡(luò)傳輸是短小但突發(fā)的。這就造成了一些問題,因為基礎(chǔ)傳輸(TCP)是針對大型、流式下載進(jìn)行優(yōu)化的。讓我們深入地看一看這些網(wǎng)絡(luò)請求。
線上資源請求的生命周期
W3C的瀏覽時序規(guī)范(Navigation Timing specification)提供了一個瀏覽器API,讓我們可以看到瀏覽器中每項請求的生命周期背后的時序和性能數(shù)據(jù)。讓我們看看這些組成部分,每一塊都是影響最佳用戶體驗的關(guān)鍵點:
圖1.1 瀏覽時序圖
[!--empirenews.page--]
對于一個網(wǎng)絡(luò)資源的URL,瀏覽器首先會檢查其本地緩存和應(yīng)用程序緩存。如果你此前獲取過該資源,并且提供了適當(dāng)?shù)木彺鏄?biāo)頭(Expires, Cache-Control等),則我們可能被允許使用本地副本來響應(yīng)原請求——最快的請求就是不請求?;蛘?,如果我們需要重新校驗該資源(如果資源已過期),或是我們根本從未獲取過該資源,那么就必須發(fā)起一個高開銷的網(wǎng)絡(luò)請求。
有了主機名稱和資源路徑,Chrome首先檢查現(xiàn)有的已開啟連接中是否有可以重用的——socket按照{(diào)scheme, host, port}的格式儲存在池中?;蛘?,如果你已經(jīng)配置了代理,或指定了代理自動配置腳本(PAC),那么Chrome就會通過適當(dāng)?shù)拇韥頇z查連接。PAC腳本允許基于URL的不同代理或其他指定規(guī)則,它們都可以有自己的socket池。最后,如果上述條件都不滿足,則請求必須通過將主機名解析為IP地址的方式發(fā)起,也稱為DNS查詢。
如果幸運的話,主機名可能已經(jīng)在緩存當(dāng)中了,此時通常只需要一次快速的系統(tǒng)調(diào)用就得到響應(yīng)。如若不然,就必須調(diào)度一個DNS查詢才能繼續(xù)下去。DNS查詢耗費的時間由網(wǎng)絡(luò)提供商、站點的熱門程度、主機名可能存在過渡緩存中的概率以及該域名的權(quán)威服務(wù)器的響應(yīng)時間所決定。換句話說,影響變量有很多,但是耗費數(shù)百毫秒來進(jìn)行一次DNS查詢也并非罕見。天啊。
圖1.2 三次握手
得到了解析后的IP地址,Chrome現(xiàn)在可以打開一個新的與目標(biāo)站點的TCP連接,這意味著我們需要進(jìn)行“三次握手”:SYN > SYN-ACK > ACK。這一交換過程又為每個新的TCP連接增加了一個往返延遲——此處沒有捷徑可走。根據(jù)客戶端與服務(wù)器的距離和所選定的路由路徑不同,這可能產(chǎn)生幾十、幾百甚至幾千毫秒的延遲。這些工作和延遲是甚至還沒有一個字節(jié)的應(yīng)用程序數(shù)據(jù)開始傳輸之前就發(fā)生的。
完成了TCP握手之后,如果我們連接的是安全站點(HTTPS),那么還要進(jìn)行SSL握手。這又增加了客戶端和服務(wù)器之間兩個往返延遲。如果SSL會話進(jìn)行了緩存,那么可以“免去”其中一次額外的往返延遲。
最后,Chrome要調(diào)度HTTP請求(圖1.1中requestStart)。服務(wù)器接收到請求之后,會處理該請求,然后通過數(shù)據(jù)流把響應(yīng)數(shù)據(jù)返給客戶端。這至少會產(chǎn)生一個往返延遲,還要算上服務(wù)器的處理時間。正常情況下這樣就結(jié)束了,但如果真正的響應(yīng)是一個HTTP重定向,那么我們可能還需要把整個過程再重走一遍。你的頁面上有不必要的重定向嗎?那你可能需要重新考慮這個決定了。
你算過這所有的延遲時間了嗎?為了便于說明問題,我們假設(shè)一個典型的寬帶連接的最壞情況:沒有本地緩存、相對較快的DNS查詢速度(50ms)、TCP握手、SSL協(xié)商、相對較快的服務(wù)器響應(yīng)時間(100ms)、80ms的往返延遲(這是美國大陸往返延遲的平均時間):
DNS需要50ms
TCP握手需要80ms(一次往返延遲)
SSL握手需要160ms(兩次往返延遲)
請求傳輸?shù)椒?wù)器需要40ms
服務(wù)器處理需要100ms
服務(wù)器返回響應(yīng)需要40ms
這樣算下來,單次請求需要470毫秒,與服務(wù)器真正處理請求的時間相比,其中80%都是網(wǎng)絡(luò)延遲開銷。實際上,470毫秒都算是一個樂觀的估計了:
如果服務(wù)器響應(yīng)不匹配初始的TCP擁塞窗口(4-15KB),還會額外產(chǎn)生一個或多個往返延遲?!咀?】
如果我們需要獲取缺失證書或者需要執(zhí)行在線證書狀態(tài)檢查(OCSP),SSL延遲得還會更厲害,因為這兩種情況都需要一次全新的TCP連接,這又增加了成百上千毫秒的額外延遲。
“足夠快”有多快?
DNS、握手、往返延遲的網(wǎng)絡(luò)開銷是決定前例中總時間的因素——服務(wù)器響應(yīng)時間僅占總延遲的20%。但是,從大局看,這些延遲重要嗎?如果你正在閱讀本文,你很可能已經(jīng)知道了答案:不但有影響,而且影響很大。
過去一些用戶體驗的研究描述了我們作為用戶對應(yīng)用程序(在線或離線)的響應(yīng)速度作何預(yù)期:
表1.1 用戶對延遲的感知
表1.1還能夠解釋網(wǎng)絡(luò)性能領(lǐng)域中一條不成文的經(jīng)驗法則:如果不能直接呈現(xiàn)頁面,至少也要在250毫秒以內(nèi)提供視覺上的反饋以保持用戶不會失去興趣。其他因素也會影響速度。對Google、Amazon、Microsoft和數(shù)千個其他站點的研究顯示,額外的延遲會直接影響站點的獲利能力:速度快的網(wǎng)站能生成更多的頁面請求,用戶參與度也更高,從而轉(zhuǎn)化率也更高。
所以,我們知道了,最佳的延遲應(yīng)該控制在250毫秒,但我們在前例中看到,DNS查詢、TCP和SSL握手再加上請求傳遞的時間總共有370毫秒。我們已經(jīng)超出50%了,這還是我們沒算上服務(wù)器處理時間的情況!
對于大多數(shù)用戶乃至一些網(wǎng)絡(luò)開發(fā)者來說,DNS、TCP和SSL的延遲是完全透明的(無須關(guān)心的),它們是在網(wǎng)絡(luò)層協(xié)商的,而我們極少深入到甚至不會去想這個層面的事。但是,這其中的每一步對整體用戶體驗都是非常關(guān)鍵的,因為每增加額外的網(wǎng)絡(luò)請求都會增加幾十或幾百毫秒的延遲。這就是為什么Chrome的網(wǎng)絡(luò)棧要比一個簡單的socket處理器復(fù)雜的多得多。
找到了癥結(jié)所在,我們繼續(xù)來看一些實現(xiàn)細(xì)節(jié)。
Chrome的網(wǎng)絡(luò)棧概覽
多進(jìn)程架構(gòu)
Chrome的多進(jìn)程架構(gòu)對各個網(wǎng)絡(luò)請求如何在瀏覽器中進(jìn)行處理具有重大影響。在底層,Chrome實際上支持四種不同的執(zhí)行模型用于確定如何進(jìn)行進(jìn)程的分配。
默認(rèn)情況下,桌面上的Chrome瀏覽器使用“站點對應(yīng)進(jìn)程”模型,將不同站點隔離開來,而把同一站點的所有實例分組在同一進(jìn)程中。不過為了簡便起見,我們假設(shè)最簡單的情況:每個打開的標(biāo)簽頁對應(yīng)一個單獨的進(jìn)程。從網(wǎng)絡(luò)性能的角度看,這種差別并不重要,但“標(biāo)簽頁對應(yīng)進(jìn)程”模型要容易理解得多。
圖1.3 多進(jìn)程架構(gòu)
這個架構(gòu)為每個標(biāo)簽頁配給一個專用的呈現(xiàn)進(jìn)程。每個呈現(xiàn)進(jìn)程包含Blink布局引擎和V8 JavaScript引擎,配合上一些膠水代碼把這幾個部分(再加上其他一些部分)聯(lián)系起來?!咀?】
這些呈現(xiàn)進(jìn)程的每一個都在一個沙盒環(huán)境內(nèi)執(zhí)行,這個環(huán)境對用戶計算機——包括網(wǎng)絡(luò),只有有限的訪問權(quán)限。要獲得訪問這些資源的權(quán)限,每個呈現(xiàn)進(jìn)程要與主瀏覽器進(jìn)程(或稱為內(nèi)核進(jìn)程)進(jìn)行通信,由內(nèi)核進(jìn)程負(fù)責(zé)管理每個呈現(xiàn)器的安全和權(quán)限策略。
進(jìn)程間通信和多進(jìn)程資源加載
Chrome中呈現(xiàn)器和內(nèi)核進(jìn)程之間的所有通信都是通過進(jìn)程間通信(IPC)完成的。在Linux和OS X上使用的是socketpair,這個方法提供一個命名的管道傳輸進(jìn)行異步通信。來自呈現(xiàn)器的每條消息經(jīng)過序列化處理再傳給專用的I/O線程,再由它將其派發(fā)給主瀏覽器進(jìn)程。在接收端,內(nèi)核進(jìn)程提供一個過濾接口,允許Chrome攔截應(yīng)該由網(wǎng)絡(luò)棧處理的資源IPC請求(參見ResourceMessageFilter),
圖1.4 進(jìn)程間通信
[!--empirenews.page--]
這種架構(gòu)的一個優(yōu)點是,所有的資源請求都完全在I/O線程上處理,用戶接口產(chǎn)生的活動與網(wǎng)絡(luò)事件之間互不干擾。資源過濾器運行在瀏覽器進(jìn)程中的I/O線程中,截獲資源請求消息,并將其轉(zhuǎn)發(fā)給瀏覽器進(jìn)程中的ResourceDispatcherHost【注3】單例。
這個單例接口允許瀏覽器控制各個呈現(xiàn)器的網(wǎng)絡(luò)訪問權(quán)限,它還能實現(xiàn)高效和一致性的資源共享。一些例子包括:
socket池和連接限制:瀏覽器能夠?qū)γ總€profile、代理和{scheme, host, port}組所對應(yīng)的已開啟socket數(shù)量進(jìn)行限制(分別為256、32和6個)。注意,按照這個規(guī)則,同一{host, port}最多可以進(jìn)行6個HTTP和6個HTTPS連接。
socket重用:持久性的TCP連接會在請求處理之后在socket池中保留一段時間,以供連接重用,這樣能夠避免發(fā)起新的連接額外帶來的DNS、TCP和SSL(如有需要)的啟動開銷。
socket后期綁定:當(dāng)socket準(zhǔn)備好分派應(yīng)用程序請求時,請求才與基礎(chǔ)的TCP連接綁定起來,這樣一來可以獲得更好的請求次序優(yōu)化(例如:當(dāng)socket在連接中時,更高優(yōu)先級的請求到達(dá)),更大的流量(例如:在現(xiàn)有socket可用而新連接正在打開時,重用“剛使用過”的TCP連接)以及TCP預(yù)連接的通用機制和其他一些優(yōu)化。
一致的會話狀態(tài):所有呈現(xiàn)進(jìn)程的身份鑒證、cookies和緩存數(shù)據(jù)都是共享的。
全局性資源和網(wǎng)絡(luò)優(yōu)化:瀏覽器可以從所有呈現(xiàn)進(jìn)程和未完成請求的全局做出決策。例如,對前景標(biāo)簽頁發(fā)起的請求賦予網(wǎng)絡(luò)優(yōu)先級。
預(yù)測性優(yōu)化:通過觀測所有的網(wǎng)絡(luò)流量情況,Chrome能夠構(gòu)建和修正預(yù)測性模型提升性能。
就呈現(xiàn)進(jìn)程而言,它只是通過IPC發(fā)送資源請求消息,這個請求被打上對應(yīng)瀏覽器進(jìn)程的唯一請求ID,剩下的部分都是由瀏覽器內(nèi)核進(jìn)程處理的。
跨平臺資源獲取
跨Linux、Windows、OS X、Chrome OS、Android和iOS等不同平臺的可移植性是Chrome網(wǎng)絡(luò)棧實現(xiàn)中的一個重要問題。要解決這個挑戰(zhàn)性的問題,網(wǎng)絡(luò)棧被實現(xiàn)為一個幾乎單線程(有單獨的緩存和代理線程)的跨平臺庫,使Chrome可以重用相同的基礎(chǔ)設(shè)施并提供相同的性能優(yōu)化水平,更有機會進(jìn)行跨平臺的優(yōu)化。
當(dāng)然,所有的代碼都是開源的,可以在src/net子目錄中找到。我們不打算詳細(xì)剖析每個部分,但是代碼格局本身就帶有很大的信息量,告訴我們它的功能和結(jié)構(gòu)。表1.2中是一些例子。
表1.2 Chrome的組件
這些組件的代碼都讓人忍不住想好好讀一讀,它們文檔完備,每個組件你都能找到很多的單元測試。
移動平臺的架構(gòu)和性能
移動端瀏覽器的使用正在以指數(shù)級增長,即使按照保守預(yù)測,它也會在不遠(yuǎn)的將來完全取代桌面瀏覽。所以不言而喻,提供優(yōu)化的移動端訪問體驗一直是Chrome團隊的首要任務(wù)之一。在2012年初,Chrome for Android發(fā)布,數(shù)月后Chrome for iOS發(fā)布。
關(guān)于Chrome的移動端版本,第一件需要注意的事是,它并不是簡單地直接在桌面瀏覽器基礎(chǔ)上做一些調(diào)整——那樣并不能得到最好的用戶體驗。從本質(zhì)講,移動端環(huán)境的資源更加局限,而且有很多根本不同的操作參數(shù):
桌面用戶通過鼠標(biāo)來瀏覽,可以進(jìn)行窗口重疊,屏幕更大,幾乎沒有電量的約束,網(wǎng)絡(luò)連接通常更穩(wěn)定,能夠訪問更大的存儲和內(nèi)存池。
移動端用戶使用觸摸和手勢瀏覽,屏幕更小,受制于電池和電量的約束,往往使用流量計量的網(wǎng)絡(luò),本地存儲和內(nèi)存也較為有限。
此外,并不存在一個“典型移動設(shè)備”。不同硬件能力的各種設(shè)備五花八門,要提供最佳性能,Chrome必須適應(yīng)每種設(shè)備的操作約束條件。所幸,Chrome有多種執(zhí)行模型正好可以實現(xiàn)這一點。
在Android設(shè)備上,Chrome沿用了桌面版本中相同的多進(jìn)程架構(gòu)——即一個瀏覽器進(jìn)程多個呈現(xiàn)進(jìn)程。一個區(qū)別是,由于移動設(shè)備的內(nèi)存有限,Chrome可能不能為每個開啟的標(biāo)簽頁運行專用的呈現(xiàn)器。Chrome是根據(jù)可用內(nèi)存和設(shè)備的其他約束條件確定一個最優(yōu)的呈現(xiàn)進(jìn)程數(shù)量,由多個標(biāo)簽頁共享呈現(xiàn)進(jìn)程。
當(dāng)只有最少資源可用時,或者Chrome無法運行多進(jìn)程時,它也可以切換回使用單進(jìn)程、多線程處理模型。實際上,在iOS設(shè)備上,由于基礎(chǔ)平臺對沙盒的限制,它就是這樣實現(xiàn)的——運行多線程的單一進(jìn)程。
網(wǎng)絡(luò)性能方面呢?首先,Chrome在Android和iOS上使用和其他版本相同的網(wǎng)絡(luò)棧。這樣保證所有平臺都有相同的網(wǎng)絡(luò)優(yōu)化,Chrome由此獲得顯著的性能優(yōu)勢。但是,如推測優(yōu)化技術(shù)、socket超時設(shè)定與管理邏輯、緩存大小等這樣的變量,是有所不同的并且會根據(jù)設(shè)備功能和所用網(wǎng)絡(luò)時時調(diào)整。
例如,為了節(jié)約電池電量,移動端Chrome可能選擇使用閑置socket的懶惰關(guān)閉方式——即僅當(dāng)開啟新socket時才關(guān)閉舊的,這樣來盡可能減少使用廣播模組。同樣地,因為預(yù)呈現(xiàn)(見下文)可能需要大量的網(wǎng)絡(luò)和處理器資源,所以通常僅當(dāng)用戶使用WiFi時才進(jìn)行。
優(yōu)化移動端瀏覽體驗是Chrome開發(fā)團隊的首要優(yōu)先任務(wù)之一,我們可以期待在未來幾個月或幾年內(nèi)看到新的改進(jìn)。實際上,這是一個值得單獨行文介紹的內(nèi)容——或許在POSA系列的下一版中就會出現(xiàn)。
使用Chrome預(yù)測器進(jìn)行推測優(yōu)化
Chrome會隨著你的使用變得越來越快。這項本領(lǐng)是借助Predictor單例對象實現(xiàn)的,它在瀏覽器內(nèi)核進(jìn)程中被實例化,其唯一職責(zé)是觀測網(wǎng)絡(luò)模式,并學(xué)習(xí)和預(yù)測未來可能出現(xiàn)的用戶行為。Predictor處理信號的一些例子有:
用戶鼠標(biāo)在一個鏈接上懸停,很好地預(yù)示了接下來可能發(fā)生的瀏覽行為,Chrome可以發(fā)起一個推測的目標(biāo)主機DNS查詢,還有可能開始TCP握手,以提升速度。待用戶實際點擊時,這平均還需要約200毫秒的時間,我們很有可能已經(jīng)完成了DNS和TCP的步驟,這就為這次瀏覽減少了數(shù)百毫秒的額外延遲時間。
在Omnibox(URL)欄中鍵入內(nèi)容將觸發(fā)高概率建議,也會觸發(fā)DNS查詢、TCP預(yù)連接甚至在隱藏的標(biāo)簽頁中預(yù)呈現(xiàn)該頁面。
我們都有一個每天訪問的喜愛站點清單。Chrome可以學(xué)習(xí)這些站點的子資源并推測性地進(jìn)行預(yù)解析,甚至可能預(yù)獲取這些資源來加速瀏覽。
Chrome會隨著你的使用,逐步學(xué)習(xí)網(wǎng)絡(luò)的拓?fù)浣Y(jié)構(gòu)和你的瀏覽模式。如果順利,它可以為每次瀏覽減少數(shù)百毫秒的延遲,讓用戶更加接近“頁面即刻加載”的理想狀態(tài)。為了實現(xiàn)這一點,Chrome使用了四個核心的優(yōu)化技術(shù),見表1.3
表1.3 Chrome所使用的網(wǎng)絡(luò)優(yōu)化技術(shù)
每個觸發(fā)這些技術(shù)的決定都是在大量約束條件下優(yōu)化判斷的。畢竟,每一項優(yōu)化都是推測性的,這意味著如果運用失當(dāng),可能會導(dǎo)致不必要任務(wù)和網(wǎng)絡(luò)流量,更糟的是,還有可能對用戶實際瀏覽行為的加載時間產(chǎn)生負(fù)面效果。
Chrome是如何解決這個問題的呢?預(yù)測器會盡可能多地吸收信號,包括用戶產(chǎn)生的行為、歷史瀏覽數(shù)據(jù)以及來自呈現(xiàn)器和網(wǎng)絡(luò)棧本身的信號。
與ResourceDispatcherHost負(fù)責(zé)協(xié)調(diào)Chrome中所有網(wǎng)絡(luò)活動的情況類似,Predictor對象也在Chrome內(nèi)部創(chuàng)建了對一些用戶和網(wǎng)絡(luò)產(chǎn)生活動的過濾器:
IPC頻道過濾器,監(jiān)測來自呈現(xiàn)進(jìn)程的信號
為各個請求添加ConnectInterceptor對象,這樣它就能觀測每個請求的流量模式并記錄成功次數(shù)。
下面是一個實際操作的例子,呈現(xiàn)進(jìn)程可以發(fā)出一條帶有以下任何提示的消息給瀏覽器進(jìn)程,這些提示定義在ResolutionMotivation(url_info.h)中:
收到這樣的信號后,預(yù)測器的目標(biāo)是評價其成功的可能性,然后在資源可用的情況下觸發(fā)相應(yīng)行為。每條提示可能都有一個成功概率、一個優(yōu)先級和一個過期時間戳,這組數(shù)據(jù)可用于維護(hù)一個推測優(yōu)化的內(nèi)部優(yōu)先級隊列。最后,對于每條從此隊列發(fā)出的請求,預(yù)測器還能夠跟蹤記錄其成功率,這又被用于未來決策的優(yōu)化中。
Chrome網(wǎng)絡(luò)架構(gòu)概要
Chrome使用多進(jìn)程架構(gòu),將呈現(xiàn)進(jìn)程與瀏覽器進(jìn)程隔離開。
Chrome保持資源調(diào)度器的一個單一實例,它被所有呈現(xiàn)進(jìn)程所共享,運行在瀏覽器內(nèi)核進(jìn)程中。
網(wǎng)絡(luò)棧是一個跨平臺、幾乎單線程的庫。
網(wǎng)絡(luò)棧使用非阻塞操作來管理所有網(wǎng)絡(luò)操作。
共享的網(wǎng)絡(luò)??蓪崿F(xiàn)高效的資源次序優(yōu)先化、重用并使瀏覽器可以在所有運行的進(jìn)程之間進(jìn)行全局性的優(yōu)化。
各個呈現(xiàn)進(jìn)程通過IPC與資源調(diào)度器通信。
資源調(diào)度器通過自定義的IPC過濾器截獲資源請求。
預(yù)測器截獲資源請求和響應(yīng)通信來學(xué)習(xí)和優(yōu)化未來的網(wǎng)絡(luò)請求。
預(yù)測器根據(jù)所學(xué)的瀏覽模式可能推測性地安排DNS、TCP甚至資源請求的計劃,當(dāng)用戶實際發(fā)生行為時節(jié)省數(shù)百毫秒的時間。
瀏覽器會話的生命周期
有了對Chrome網(wǎng)絡(luò)棧架構(gòu)的一個概括性認(rèn)識后,我們接下來仔細(xì)研究一下瀏覽器內(nèi)部實施的各種面向用戶的優(yōu)化。具體而言,假設(shè)我們剛創(chuàng)建了一個新的Chrome profile,準(zhǔn)備好開始了。
[!--empirenews.page--]
優(yōu)化冷啟動體驗
當(dāng)你第一次加載瀏覽器時,它對你的喜愛站點和瀏覽模式一無所知。但是,我們中的很多人都會在瀏覽器冷啟動后做同樣的事:我們會瀏覽我們的電郵收件箱、喜愛的新聞?wù)军c、社交網(wǎng)站、內(nèi)網(wǎng)入口等等。具體的站點可能因人而異,但是這些會話的相似之處使Chrome的Predictor可以加速你的冷啟動過程。
Chrome會記住用戶在瀏覽器啟動后前10個最有可能訪問的主機名——注意這并不是前10個全局目標(biāo)站點,而特指全新開啟瀏覽器之后的目標(biāo)站點。瀏覽器加載時,Chrome可以為這些可能的目標(biāo)站點觸發(fā)DNS預(yù)獲取行為。如果你對此感興趣,可以打開一個新標(biāo)簽頁瀏覽地址chrome://dns,看一看你自己的啟動主機名列表。在頁面頂端,你會找到你profile的前10個最可能啟動站點。
圖1.5 啟動DNS
圖1.5的截圖是我的Chrome profile的例子。我通常是如何開始瀏覽的呢,如果我在寫文章,比如現(xiàn)在這一篇,可能會頻繁訪問Google Docs。果不其然,我們在列表中看到很多Google的主機名。
使用Omnibox優(yōu)化交互過程
Chrome的創(chuàng)新之一是引入了Omnibox,它與此前的地址欄不同,可以處理目標(biāo)站點URL之外的很多東西。除了記住用戶曾經(jīng)訪問過的頁面的URL之外,它還提供完整的對歷史記錄的文本檢索功能,與你所選擇的搜索引擎的集成性也較好。
隨著用戶在其中鍵入內(nèi)容,Omnibox會自動提供建議的行為,可能是基于你的瀏覽歷史的URL或者是一次檢索查詢。在后臺,每個建議行為都根據(jù)查詢結(jié)果和歷史表現(xiàn)進(jìn)行評分。實際上,Chrome允許我們通過訪問chrome://predictors來查看這些數(shù)據(jù)。
圖1.6 Omnibox的URL預(yù)測
Chrome會維護(hù)用戶輸入前綴、建議行為以及每一記錄的命中率的歷史記錄。在我的profile中,你可以看到當(dāng)我在Omnibox中輸入“g”時,有76%的可能性我是要訪問Gmail。而當(dāng)我加了一個“m”之后(變成“gm”),則置信水平上升到99.8%,實際上,在記錄的412次訪問中,我只有一次在輸入“gm”之后沒有訪問Gmail。
這與網(wǎng)絡(luò)棧有什么關(guān)系呢?可能備選站點中黃色和綠色的記錄也是ResourceDispatcher的重要信號。如果我們有一個可能的備選站點(黃色),Chrome可能為該目標(biāo)主機觸發(fā)DNS預(yù)獲取。如果我們有一個高度確信的備選站點(綠色),那么Chrome可能還會在主機名解析之后觸發(fā)TCP預(yù)連接。最后,如果這兩步都做完時用戶還沒做出最終決定,那么Chrome甚至可能在隱藏的標(biāo)簽頁中預(yù)呈現(xiàn)整個頁面。
另一方面,如果根據(jù)過去的瀏覽歷史沒有為所輸入的前綴找到較好的匹配,那么Chrome預(yù)計可能會發(fā)生檢索請求,會向你的搜索引擎發(fā)起DNS預(yù)獲取和TCP預(yù)連接。
一個普通用戶需要花費數(shù)百毫秒來填寫查詢內(nèi)容,評價所給出的自動補全建議。在后臺,Chrome能夠預(yù)獲取、預(yù)連接,并在某些情況下對頁面進(jìn)行預(yù)呈現(xiàn),這樣當(dāng)用戶準(zhǔn)備好按下“輸入”鍵時,很多網(wǎng)絡(luò)延遲已經(jīng)被消除了。
優(yōu)化緩存性能
最好最快的請求是不發(fā)出請求。當(dāng)我們說到性能時必然會涉及緩存的問題——你正在為你網(wǎng)頁上的所有資源提供Expires、ETag、Last-Modified和Cache-Control這些響應(yīng)標(biāo)頭,對吧?如果你沒有這樣做,請立刻去改。我們會等你。
Chrome的內(nèi)部緩存有兩種不同的實現(xiàn)方式:一種是由本地磁盤所支持,另一種是把所有內(nèi)容存儲在內(nèi)存中。內(nèi)存實現(xiàn)方式用于匿名瀏覽模式,當(dāng)你關(guān)閉窗口后會把痕跡清除干凈。兩種方式都實現(xiàn)相同的內(nèi)部接口(disk_cache:Backend和disk_cache:Entry),這極大地簡化了架構(gòu)并且——如果你非要堅持的話——允許你很方便地試驗?zāi)阕约旱木彺鎸崿F(xiàn)。
內(nèi)部來看,磁盤緩存實現(xiàn)了其自己的數(shù)據(jù)結(jié)構(gòu),它們都被存儲在你的profile的一個單獨的緩存文件夾中。這個文件夾中有索引文件,它們在瀏覽器啟動時進(jìn)行內(nèi)存映射,還有數(shù)據(jù)文件,它們存儲了實際數(shù)據(jù)以及HTTP標(biāo)頭和其他簿記信息【注4】。最后,在緩存回收機制上,磁盤緩存會維護(hù)一個最近最少使用(LRU)緩存,它會把訪問頻度和資源新舊度等排序量化指標(biāo)考慮進(jìn)去。
如果你對Chrome的緩存狀態(tài)很感興趣,可以打開新標(biāo)簽頁訪問chrome://net-internals/#httpCache。或者,如果你想看看真實的HTTP元數(shù)據(jù)以及緩存的響應(yīng),也可以訪問chrome://cache,其中列示了緩存中當(dāng)前可用的所有資源。在該頁面中,檢索一項資源之后可以點擊URL查看緩存的標(biāo)頭和響應(yīng)的具體字節(jié)內(nèi)容。
使用預(yù)獲取優(yōu)化DNS
我們已經(jīng)在一些地方提到過DNS預(yù)解析,所以在我們深入介紹它的實現(xiàn)方式之前,先回憶一下哪些情況下為什么會觸發(fā)它:
運行在呈現(xiàn)進(jìn)程中的Blink文檔解析器,可以提供當(dāng)前頁面上所有鏈接的主機名清單,Chrome可以從中選擇提前解析。
呈現(xiàn)進(jìn)程會將鼠標(biāo)懸停和“按下按鍵”事件作為用戶意圖進(jìn)行瀏覽的前期信號,從而觸發(fā)預(yù)解析。
Omnibox根據(jù)高度可能的建議,可能觸發(fā)解析請求。
Predictor基于過去的瀏覽和資源請求數(shù)據(jù),可能請求主機名解析。
頁面所有者可能明確指示Chrome應(yīng)該預(yù)解析哪些主機名。
所有情況下,DNS預(yù)解析都被作為提示來處理。Chrome并不保證預(yù)解析必然進(jìn)行,而是綜合運用各個信號與其自身的預(yù)測器來評估這條提示并動態(tài)決策。在“最壞情況”下,如果Chrome未能及時完成主機名預(yù)解析,用戶就要等待顯式DNS解析,接著是TCP連接時間,最后是實際資源獲取。但是,如果出現(xiàn)了這種情況,預(yù)測器會進(jìn)行記錄并相應(yīng)調(diào)整其未來決策——隨著你的持續(xù)使用,它會變得更快更聰明。
我們此前沒有提到的一項優(yōu)化是Chrome能夠?qū)W習(xí)每個站點的拓?fù)浣Y(jié)構(gòu),并運用這項信息來加速未來的訪問過程。具體而言,回憶一下,一個頁面平均由88項資源組成,由15個以上不同的主機發(fā)送。每一次你進(jìn)行瀏覽,Chrome會記錄頁面上的熱門資源的主機名,在以后的訪問中,它可能會對其中一些或全部選擇觸發(fā)DNS預(yù)解析甚至TCP預(yù)連接。
訪問chrome://dns并檢索你profile對應(yīng)的熱門站點主機名,可以查看Chrome所保存下來的子資源主機名。在上面的例子中,你可以看到Chrome記錄了Google+的6個子資源主機名,還統(tǒng)計了DNS預(yù)解析觸發(fā)或TCP預(yù)連接執(zhí)行的次數(shù),還有會由各項處理的請求的估計值。這些內(nèi)部記錄幫助Chrome預(yù)測器實現(xiàn)優(yōu)化。
除了這些內(nèi)部信號之外,站點的所有者也能在頁面中嵌入附加的標(biāo)記請求瀏覽器預(yù)解析一個主機名:
[!--empirenews.page--]
為何不直接依靠瀏覽器的自動機制呢?有些時候,你可能希望解析一個頁面中任何地方都沒有提到過的主機名。重定向就是一個典型的例子:鏈接可能指向一個主機——就如同一項分析追蹤服務(wù)一樣——這個主機再把用戶重定向至真正的目標(biāo)站點。Chrome依靠自身是無法推斷出這種模式的,但你可以手動提供一條提示幫助它,讓瀏覽器提前解析真實目標(biāo)站點的主機名。
這一切在后臺是如何實現(xiàn)的呢?和我們討論過的其他優(yōu)化手段一樣,這個問題的答案也取決于Chrome的版本,因為開發(fā)團隊一直試驗新的、更好的方式來提升性能。但是,不嚴(yán)格地說,Chrome內(nèi)部的DNS基礎(chǔ)設(shè)施有兩個主要的實現(xiàn)方式。過去,Chrome依靠操作平臺無關(guān)的系統(tǒng)調(diào)用getaddrinfo,并把DNS查詢的實際職責(zé)交由操作系統(tǒng)完成。但是,這種方式正逐步被Chrome自己實現(xiàn)的異步DNS解析器所替代。
依靠操作系統(tǒng)的原有方式具有其優(yōu)點:代碼少而簡單,并能夠利用操作系統(tǒng)的DNS緩存。但是,getaddrinfo也是一個阻塞型的系統(tǒng)調(diào)用,這意味著Chrome必須創(chuàng)建并維護(hù)一個專用的worker線程池,才能夠?qū)崿F(xiàn)多條并行查詢。這個未連接池最多只能容納六個線程,這個上限是基于硬件的最小公分母得出的經(jīng)驗數(shù)字——這樣并行請求的數(shù)量超出的話,有些用戶的路由器就會過載。
對于使用worker池的預(yù)解析,Chrome就只是調(diào)度getaddrinfo調(diào)用,這會一直阻塞worker線程,直至響應(yīng)就緒后馬上丟棄所返回的結(jié)果,并開始處理下一條預(yù)獲取請求。結(jié)果由操作系統(tǒng)的DNS緩存來存儲,未來實際進(jìn)行g(shù)etaddrinfo查詢時,它會立即返回響應(yīng)。這種方式簡單有效,實踐中的表現(xiàn)也不錯。
但這還不夠好。getaddrinfo調(diào)用有很多有用信息不向Chrome公開,比如每條記錄的生存時間(TTL)時間戳,以及DNS緩存本身的狀態(tài)。為了提升性能,Chrome團隊決定自己來實現(xiàn)跨平臺的異步DNS解析器。
圖1.7 啟用異步DNS解析器
通過把DNS解析放到Chrome內(nèi)部來處理,新的異步解析器可以實現(xiàn)一些新的優(yōu)化手段:
更好控制重傳計時器,能夠并行執(zhí)行多條查詢
記錄生存時間的可見性,使得Chrome能提前刷新熱點記錄
更好的雙棧實現(xiàn)(IPv4和IPv6)行為
基于RTT或其他信號的,對不同服務(wù)器的故障切換
以上所有以及其他很多想法都是在Chrome內(nèi)持續(xù)試驗并優(yōu)化的。這就必然涉及一個問題:我們?nèi)绾瘟私獠⒑饬窟@些想法的效果呢?很簡單,Chrome會為每個profile分別記錄詳細(xì)的網(wǎng)絡(luò)性能統(tǒng)計數(shù)據(jù)和直方圖。要查看所收集到的DNS統(tǒng)計數(shù)據(jù),可以打開新標(biāo)簽頁,訪問chrome://histograms/DNS(見圖1.8)。
圖1.8 DNS預(yù)獲取的直方圖
上面的直方圖顯示出了DNS預(yù)獲取請求延遲的分布情況:大約50%的(最右側(cè)列)預(yù)獲取查詢在20毫秒內(nèi)完成(最左側(cè)列)。注意,這是基于最近的瀏覽會話(9869個樣本)的統(tǒng)計得到的,并屬于用戶的隱私數(shù)據(jù)。如果該用戶選擇報告Chrome的使用統(tǒng)計數(shù)據(jù),則這些數(shù)據(jù)的摘要會被匿名處理,定期反饋給開發(fā)團隊,他們就可以看到試驗的效果并進(jìn)行相應(yīng)的調(diào)整。
使用預(yù)連接優(yōu)化TCP連接管理
我們已經(jīng)預(yù)解析了主機名,按照Omnibox或Chrome預(yù)測器的估計,我們很有可能即將進(jìn)行瀏覽行為。為什么不更進(jìn)一步,也推測性地預(yù)連接到目標(biāo)主機,在用戶發(fā)出請求之前完成TCP握手的步驟呢?這樣一來,我們又能消除掉一個往返延遲,輕松省去用戶數(shù)百毫秒的時間。沒錯,TCP預(yù)連接正是這樣做的。
打開新標(biāo)簽頁訪問chrome://dns可以查看已經(jīng)觸發(fā)TCP預(yù)連接的主機。
圖1.9 展示已經(jīng)觸發(fā)TCP預(yù)連接的主機
首先,Chrome會檢查socket池,看看是否有該主機名的可用socket可供重用——存活socket會在池中留存一段時間,以避免TCP握手和慢熱啟動的懲罰時間。如果沒有socket可用,則由其發(fā)起TCP握手并放入池中。然后,當(dāng)用戶進(jìn)行瀏覽時,HTTP請求就可以立刻調(diào)度。
Chrome在chrome://net-internals#sockets中提供了一個工具可以查看Chrome中所有已開啟socket的狀態(tài)。圖1.10是相關(guān)的截圖。
圖1.10 已開啟的socket
你還可以詳細(xì)查看每個socket,檢查時間線:連接和代理時間,每個包的到達(dá)時間等等。最后要說的很重要的一點是:你也可以導(dǎo)出這些數(shù)據(jù)進(jìn)行后續(xù)分析或報告bug。具有良好的信息統(tǒng)計機制對任何優(yōu)化都是很關(guān)鍵的,chrome://net-internals就是Chrome中所有功能相互作用的集中展示——如果你還沒探索過這個功能,你應(yīng)該試試!
使用預(yù)獲取提示優(yōu)化資源加載
有時,頁面的作者能夠提供基于其站點結(jié)構(gòu)或布局附加的導(dǎo)航或頁面上下文,幫助瀏覽器優(yōu)化用戶體驗。Chrome支持兩種這樣的提示,可以嵌入頁面標(biāo)記中使用:
子資源和預(yù)獲取看起來非常類似,但是語義完全不同。當(dāng)鏈接資源指定其關(guān)系為“預(yù)獲取”時,它是告訴瀏覽器,這項資源在以后的瀏覽中可能用到。換言之,這實際上是一個跨頁面提示。而當(dāng)資源指定其關(guān)系為“子資源”時,它是提前告訴瀏覽器該資源會在當(dāng)前頁面中被用到,在該文檔后面的部分遇到它之前可以先發(fā)起請求。
可以想見,兩者的不同語義會導(dǎo)致資源加載器的行為大相徑庭。標(biāo)記預(yù)獲取的資源會被看做優(yōu)先級較低,并在當(dāng)前頁面加載完成后由瀏覽器進(jìn)行一次下載。標(biāo)記為子資源的內(nèi)容一旦遇到就會作為高優(yōu)先級資源來獲取,并與當(dāng)前頁面上的其余資源相互競爭。
這兩種提示如果使用得當(dāng)可以極大地幫助優(yōu)化你站點的用戶體驗。最后,還要注意,預(yù)獲取是HTML5規(guī)范的一部分,目前Firefox和Chrome都支持,而子資源目前只限于Chrome。
使用瀏覽器預(yù)刷新優(yōu)化資源加載
不巧的是,不是所有站點所有者都能夠或愿意在標(biāo)記中為瀏覽器提供子資源提示。而且,即使他們這樣做,我們還是要等待HTML文檔從服務(wù)器傳送過來之后才能解析這些提示,開始獲取這些必要的子資源——根據(jù)服務(wù)器響應(yīng)時間和客戶端與服務(wù)器間的延遲,這可能需要耗費數(shù)百乃至數(shù)千毫秒。
但是,我們前面看到,Chrome已經(jīng)通過學(xué)習(xí)常用資源的主機名來執(zhí)行DNS預(yù)獲取了。那么,為什么不如法炮制,更進(jìn)一步:執(zhí)行DNS查詢,使用TCP預(yù)連接然后也推測性地預(yù)獲取資源呢?沒錯,這就是預(yù)刷新的作用:
用戶發(fā)起對目標(biāo)URL的請求
Chrome詢問預(yù)測器其所學(xué)習(xí)到的與目標(biāo)URL相關(guān)的子資源,并發(fā)起DNS預(yù)獲取、TCP預(yù)連接和資源預(yù)刷新等一連串行為
如果所學(xué)到的子資源在緩存中,則將其從磁盤加載到內(nèi)存中
如果所學(xué)到的子資源缺失,或者已過期,則進(jìn)行一次網(wǎng)絡(luò)請求
資源預(yù)刷新是展示Chrome中每個試驗性優(yōu)化手段的工作流的絕佳例子——理論上講,一項優(yōu)化應(yīng)該使性能得到提升,但是也涉及很多因素的此消彼長。只有一種方式能夠可靠地確定一項優(yōu)化是不是有效,是不是適合于Chrome:先實現(xiàn)它,并在一些預(yù)先發(fā)布的渠道(真正的網(wǎng)絡(luò)、真實瀏覽模式、真人用戶)上進(jìn)行A/B試驗。
在2013年初,Chrome團隊還處于討論這種實現(xiàn)的早期階段。如果根據(jù)所收集的結(jié)果它能奏效,我們我們可能會在年內(nèi)晚些時候在Chrome中看到預(yù)刷新。Chrome的網(wǎng)絡(luò)性能優(yōu)化從未止步——開發(fā)團隊一直在試驗新的方法、創(chuàng)意和技術(shù)。
[!--empirenews.page--]
使用預(yù)呈現(xiàn)優(yōu)化瀏覽
目前為止我們介紹過的每一項優(yōu)化都是幫助減少用戶進(jìn)行瀏覽的直接請求和標(biāo)簽頁上呈現(xiàn)最終頁面之間的延遲的。但是,要真正得到即刻展示頁面的體驗,需要什么呢?根據(jù)我們之前看到的UX數(shù)據(jù),這樣的交互時間需要低于100毫秒,這樣,留給網(wǎng)絡(luò)延遲的余地可不算多。我們怎么才能在100毫秒內(nèi)把呈現(xiàn)好的頁面展示出來呢?
當(dāng)然,你已經(jīng)知道答案了,因為這是很多用戶所用的共同模式:如果你打開多個標(biāo)簽頁,標(biāo)簽頁間的切換就是即刻的,比在一個前景標(biāo)簽頁中瀏覽同樣資源之間的等待要快得多。那么,如果瀏覽器提供一個API來實現(xiàn)這一點呢?
你可能猜到了,這就是Chrome中的預(yù)呈現(xiàn)。不是如“預(yù)獲取”提示實現(xiàn)的那樣只下載單項資源,“預(yù)呈現(xiàn)”屬性命令Chrome在隱藏標(biāo)簽頁中預(yù)呈現(xiàn)頁面以及所有子資源。隱藏標(biāo)簽頁本身對用戶不可見,但當(dāng)用戶觸發(fā)瀏覽行為時,該標(biāo)簽頁就會從后臺切換出來實現(xiàn)“即刻體驗”。
想看看這是如何實現(xiàn)的嗎?可以訪問prerender-test.appspot.com上還在開發(fā)中的演示版,要查看你的profile的預(yù)呈現(xiàn)頁面的歷史記錄和狀態(tài),訪問:chrome://net-internals/#prerender。(見圖1.11)
圖1.11 當(dāng)前profile的預(yù)呈現(xiàn)頁面
可以預(yù)見的是,在隱藏標(biāo)簽頁中呈現(xiàn)完整頁面可能會耗費CPU和網(wǎng)絡(luò)的大量資源,所以僅當(dāng)我們高度確信應(yīng)該使用隱藏標(biāo)簽頁時才應(yīng)該使用。例如,當(dāng)你使用Omnibox時,對高度確信的建議可能會觸發(fā)預(yù)呈現(xiàn)。類似地,Google搜索如果估計認(rèn)為它的第一條檢索結(jié)果是高度確信的目標(biāo)站點,有時會在其標(biāo)記中添加預(yù)呈現(xiàn)提示(也稱為Google即開頁面)。
你還可以為自己的站點添加預(yù)呈現(xiàn)提示。在你這樣做之前,請先了解并記住預(yù)呈現(xiàn)過程具有的以下局限之處:
所有線程總共至多只能有一個預(yù)呈現(xiàn)標(biāo)簽頁
HTTPS和需要HTTP身份鑒證的頁面不能使用
如果所請求資源或其任何子資源需要進(jìn)行非冪等請求(只允許GET請求),預(yù)呈現(xiàn)會被放棄
所有資源都以最低的網(wǎng)絡(luò)優(yōu)先級進(jìn)行獲取
該頁面以最低的CPU優(yōu)先級進(jìn)行呈現(xiàn)
如果內(nèi)存需求超過100MB則該頁面會被放棄
插件初始化被推遲,如果出現(xiàn)了HTML5的多媒體元素則預(yù)呈現(xiàn)被放棄
換言之,預(yù)呈現(xiàn)不保證一定發(fā)生,并只適用于安全的頁面。此外,因為JavaScript和其他邏輯可能在隱藏頁面中被執(zhí)行,實踐中最好使用頁面可見性API來檢測一下該頁面是否可見——這是你本就應(yīng)該做的。
Chrome會隨著你的使用越來越快
無需多言,Chrome的網(wǎng)絡(luò)棧絕非一個簡單的socket管理器。我們這次走馬觀花的概述介紹了在網(wǎng)站瀏覽的背后多層次的透明運行的優(yōu)化手段。Chrome對網(wǎng)站拓?fù)浣Y(jié)構(gòu)和你的瀏覽模式了解越是深入,它的效果就越好。就像魔法一樣,Chrome會隨著你的使用越來越快。可你知道它并不是魔法,你了解這背后的機制。
最后,還要注意一點,Chrome團隊持續(xù)試驗著優(yōu)化性能的新想法——這個過程從未停止。在你閱讀本文時,就可能有新的試驗項目和優(yōu)化手段正在開發(fā)、測試或部署著。也許只有當(dāng)我們能夠?qū)γ總€站點每個頁面都實現(xiàn)即刻加載(小于100毫秒)時,才會停下腳步吧。在那之前,總有工作等著我們?nèi)ネ瓿伞?/p>
注釋:
第10章:《移動網(wǎng)絡(luò)性能的秘密》詳細(xì)解釋了這個問題。
如果你感興趣,Chromium的百科頁面上有詳細(xì)的介紹。
Http://code.google.com/searchframe#OAMlx_jo-ck/src/content/public/browser/resource_dispatcher_host.h&exact_package=chromium&q=ResourceDispatcherHost.
16KB以內(nèi)的資源保存在共享數(shù)據(jù)塊文件中,更大的文件在磁盤上有自己的專用文件。
英文原文:http://aosabook.org/en/posa/high-performance-networking-in-chrome.html
譯者:unione
轉(zhuǎn)載:http://www.toutiao.com/i6307361226703766018/