bigPipe 原理分析
一、什么是bigPipe?
bigPipe是由facebook提出來的一種動(dòng)態(tài)網(wǎng)頁加載技術(shù)。它將網(wǎng)頁分解成稱為pagelets的小塊,然后分塊傳輸?shù)綖g覽器端,進(jìn)行渲染。它可以有效地提升首屏渲染時(shí)間。
為了說清楚什么是bigPipe,我先需要介紹下目前的常規(guī)渲染方式,以及可以進(jìn)行優(yōu)化的方向。
二、網(wǎng)頁首屏加載方案
注:首屏加載方案指的是在服務(wù)端就已經(jīng)吐出頁面的方案,也就是說有SSR的方案,那些純客戶端渲染的方案不做比較,因?yàn)樗鼈儧]有首屏要求。
現(xiàn)有的網(wǎng)頁首屏加載方案一般會(huì)經(jīng)過以下階段:
發(fā)送http請(qǐng)求到服務(wù)端
服務(wù)端接收并分析請(qǐng)求
服務(wù)端根據(jù)請(qǐng)求從存儲(chǔ)層獲取相關(guān)數(shù)據(jù),這里可能會(huì)比較耗時(shí),比如如果首頁涉及多個(gè)模塊(廣告位、推薦、內(nèi)容列表、用戶信息、好友列表等)
服務(wù)端準(zhǔn)備好所有內(nèi)容,拼接成完整的html文檔
發(fā)送回客戶端
客戶端接收完整的html文檔
構(gòu)建dom、cssom, 生成render tree,渲染出指定頁面
如下圖所示:
以上加載方案的缺點(diǎn)是,當(dāng)2、3、4、5 步在服務(wù)端進(jìn)行的時(shí)候,瀏覽器只能是傻傻地等待,做不了任何事情!
而且第3步并行拉取業(yè)務(wù)數(shù)據(jù)在某些場(chǎng)景下(模塊多,業(yè)務(wù)場(chǎng)景復(fù)雜),是會(huì)占用比較多的時(shí)間的。而且只要其中某個(gè)模塊的數(shù)據(jù)如果拉取較慢,會(huì)拖慢整個(gè)首屏的顯示。
更糟糕的情況下,某些模塊數(shù)據(jù)相互依賴,導(dǎo)致需要串行拉取數(shù)據(jù),那造成的瀏覽器的等待則會(huì)更久。
三、優(yōu)化方案
以上方案在一些服務(wù)端渲染頁面中相當(dāng)常見,不足之處也很明顯。
而bigPipe就是針對(duì)第3、4步進(jìn)行優(yōu)化,讓服務(wù)端在準(zhǔn)備好某個(gè)模塊的數(shù)據(jù)后,立馬返回給客戶端顯示,而不必要等待完整的數(shù)據(jù)和html生成,再發(fā)送給客戶端。
客戶端在接收到某一部分內(nèi)容后,就可以開始渲染,顯示執(zhí)行(這里可以動(dòng)態(tài)請(qǐng)求需要的css,js 等等)。
如下圖,客戶端拉取業(yè)務(wù)數(shù)據(jù)和客戶端渲染頁面可以并行。如果某一部分比如廣告信息拉取超時(shí),也并不影響其他部分率先渲染顯示。
這樣,一個(gè)完整的頁面就可以拆成多個(gè)部分,分塊渲染,而無需等到拿到完整的頁面返回,再渲染。要知道, 如果要等到完整頁面返回,在這之前,瀏覽器只能是一片空白!
四、關(guān)鍵技術(shù)和原理
想要實(shí)現(xiàn)以上優(yōu)化方案,可以利用現(xiàn)成的技術(shù),所以有比較好的兼容性。
1.分段傳輸
bigPipe依賴于分段傳輸html頁面,所以這是實(shí)現(xiàn)bigPipe的一個(gè)基礎(chǔ)。
http1.1
如果在http1.1版本上實(shí)現(xiàn),那需要設(shè)置Transfer-Encoding為chunked,也就是分塊傳輸編碼。
關(guān)于分塊傳輸編碼:
分塊傳輸編碼允許服務(wù)器為動(dòng)態(tài)生成的內(nèi)容維護(hù)HTTP持久連接。在這種情況下,不能使用HTTP Content-Length頭來分隔內(nèi)容和下一個(gè)HTTP請(qǐng)求/響應(yīng),因?yàn)閮?nèi)容大小未知。
分塊編碼的好處是,在返回客戶端前不必生成完整的內(nèi)容,因?yàn)樗试S將內(nèi)容作為分塊進(jìn)行流式處理,并明確地發(fā)出內(nèi)容結(jié)尾的信號(hào),從而使連接可用于下一個(gè)HTTP請(qǐng)求/響應(yīng)。
在頭部加入 Transfer-Encoding: chunked 之后,就代表這個(gè)報(bào)文采用了分塊編碼。這時(shí),報(bào)文中的實(shí)體需要改為用一系列分塊來傳輸。
每個(gè)分塊包含十六進(jìn)制的長(zhǎng)度值和數(shù)據(jù),長(zhǎng)度值獨(dú)占一行,長(zhǎng)度不包括它結(jié)尾的 CRLF(\r\n),也不包括分塊數(shù)據(jù)結(jié)尾的 CRLF。
最后一個(gè)分塊長(zhǎng)度值必須為 0,對(duì)應(yīng)的分塊數(shù)據(jù)沒有內(nèi)容,表示實(shí)體結(jié)束。
http 2
如果你使用的是http2,那則無需設(shè)置Transfer-Encoding為chunked,因?yàn)閔ttp2本身就是支持這種分塊傳輸?shù)膮f(xié)議。
2.瀏覽器渲染原理
說到瀏覽器渲染,我們可以簡(jiǎn)單地把它歸為五個(gè)階段。(為了方便分析渲染過程,先不考慮有js的情況)
階段一:
解析html文檔,生成節(jié)點(diǎn),構(gòu)建dom樹
階段二:
在階段一中,如果遇到css(內(nèi)嵌在html文檔或者外鏈或者內(nèi)聯(lián)樣式都一樣),則會(huì)解析css文檔,生成cssom。
階段三:
階段一和階段二都是可以并行的,等到dom和cssom準(zhǔn)備好,會(huì)進(jìn)行合并,生成render tree。
階段四:
根據(jù)render tree進(jìn)行l(wèi)ayout。
階段五:
繪制到顯示區(qū)域。
整個(gè)階段如下圖所示
幸運(yùn)的是,瀏覽器并不會(huì)等解析完完整的html文檔后,才進(jìn)行l(wèi)ayout 和paint。
瀏覽器已經(jīng)對(duì)顯示html文檔進(jìn)行了優(yōu)化,會(huì)盡快將解析好的部分呈現(xiàn)給用戶。也就是,上圖所謂的一次渲染過程,在分塊傳輸?shù)臅r(shí)候,是可以多次進(jìn)行的。直到接收到</body></html>閉合標(biāo)簽。
五、實(shí)踐
bigPipe技術(shù)的基本原理在上面就已經(jīng)介紹完了。實(shí)踐都是基于以上原理而來的。
常規(guī)實(shí)踐是將頁面分成各個(gè)模塊,稱之為一個(gè)個(gè)pagelets,每個(gè)pagelets包含自己需要的模板數(shù)據(jù),css樣式和需要的js。
在傳輸pagelets之前,先將頁面主體layout傳輸?shù)娇蛻舳耍冗M(jìn)行渲染,此時(shí),用戶已經(jīng)可以看到頁面的主體框架了。
之后,再將服務(wù)端處理完的pagelets一個(gè)個(gè)返回,在客戶端渲染。如下圖:
