摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼這一節,請跟隨筆者聊一聊,網頁的分段傳輸與渲染,用一些非常規手段優化我們的網站響應速度。可以處理完一塊就返回一塊,讓瀏覽器盡早的接收到,可以先行渲染。
歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):
https://segmentfault.com/blog...
這一節,請跟隨筆者聊一聊,網頁的分段傳輸與渲染,用一些非常規手段優化我們的網站響應速度。
1 CHUNKED編碼 1.1 傳統的渲染方法 1.1.1 傳統的渲染方法怎么做?按照常理,我們渲染一張網頁,必定是網頁全部拼裝完畢,然后生成HTML字符串,傳送至客戶端。這也意味著,如果一張網頁處理的有快有慢的話,必須串行等到所有的邏輯都處理完畢。后端才能進行返回。(這也是我們目前網頁的一般邏輯)。如下面的例子,三個很慢的讀數據操作,均執行完畢后,才傳送渲染頁面。渲染效果如圖1.1.1,15s之后才傳送并渲染出頁面:
normal.php
的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出>的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出>的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出>的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據"; } $var1 = getOneData(); function getTwoData() { usleep(5000000); return "是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據"; } $var2 = getTwoData(); function getThreeData() { usleep(5000000); return "我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出>的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出>的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出>的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出>的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據"; } $var3 = getThreeData(); // 渲染模板并輸出 include("./normal.html.php");
normal.html.php
1.2.3.
圖1.1.1
上述例子,在本文后github中的normal文件夾中。
1.1.2 傳統的渲染方法有哪些弊端?如上所示,我們能看到,直出的網頁中,存在著后端數據串行,互相等待的尷尬局面。這也為我們后續的優化埋下了伏筆。
1.2 分段傳輸 1.2.1 何為分段傳輸?http1.1中引入了一個http首部,Transfer-Encoding:chunked。這個首部標識了實體采用chunked編碼傳輸,chunked編碼可以將實體分塊兒進行傳輸,并且chunked編碼的每一塊內容都會自標識長度。這給了web開發者一個啟示,如果需要多個數據,而多個數據均返回較慢的話。可以處理完一塊就返回一塊,讓瀏覽器盡早的接收到html,可以先行渲染。
1.2.2 如何分段傳輸?既然知道了我們可以將網頁一塊兒一塊兒的傳送,那么我們就可以將上面的網頁進行改造,拿好一塊兒需要的數據,便渲染一塊兒,無需等待,而模板方面,自然也要拆分為三段,供服務端拿一塊兒的模板,就渲染一塊兒出去,效果如圖1.2.2.1。
normal.php
的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出>的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出>的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出>的第一個數據我是取出的第一個數據我是取出的第一個數據我是取出的第一個數據"; } // 取出第一塊兒的數據 $var1 = getOneData(); // 渲染第一塊兒 include("./normal1.html.php"); //刷新到緩沖區,渲染第一份兒模板,傳送到客戶端 ob_flush(); flush(); function getTwoData() { usleep(5000000); return "我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出>的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出>的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出>的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出>的第二個數據我是取出的第二個數據我是取出的第二個數據我是取出的第二個數據"; } // 取出第二塊兒的數據 $var2 = getTwoData(); // 渲染第二塊兒 include("./normal2.html.php"); //刷新到緩沖區,渲染第二份兒模板,傳送到客戶端 ob_flush(); flush(); function getThreeData() { usleep(5000000); return "我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出>的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出>的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出>的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出>的第三個數據我是取出的第三個數據我是取出的第三個數據我是取出的第三個數據"; } // 獲取第三塊兒的數據 $var3 = getThreeData(); // 渲染第三塊兒 include("./normal3.html.php"); // 將第三份兒的模板,傳送到客戶端 ob_flush(); flush();
normal1.html.php
1.
normal2.html.php
2.
normal3.html.php
3.
圖1.2.2.1
上述例子,在本文后github中的chunked文件夾中。
對比圖圖1.1.1與圖1.2.2.1我們可以發現,雖然最后總的處理時長不變,但是采用了分段輸出的網頁,可以盡早的將一段HTML渲染到客戶端,這樣用戶可以使用先到達的部分。另一方面,盡早的頁面反饋,也可以減少用戶等待的焦躁情緒。綜上,使用此種優化方法,可以提速網頁的渲染速度。
1.2.3 分段傳輸小TIPs我們代碼雖然如上所述,但是讀者嘗試的時候可能會發現,并沒有什么效果。和我截圖并不一樣,還是等到15s后一起渲染出來了。這里要提醒大家一下,可能是由于nginx配置的原因。如果使用的是nginx做server的話,要使用如下配置才能看到效果。
http { .... fastcgi_buffer_size 1k; fastcgi_buffers 16 1k; gzip off; .... }
其實讀者們可以這么理解上面的配置,nginx會在攢夠一塊兒緩沖區的量后,可以將一塊兒數據發出去。上面我們配置了fastcgi_buffers 16 1k; 就是16塊兒,大小為1K的緩存。
我們的數據量太小了,連默認的一塊兒緩沖區都填不滿,沒法看到分塊兒發送的效果,所以這里我們將緩沖區給調小為1K,這樣就能1K為單位分塊兒,1K一發,體現出實驗效果了。筆者這里建議做實驗的時候,最好把gzip給關了,因為,咱們做實驗的時候數據量不大,實際使用中建議chunked與gzip均開啟(如圖1.2.3.1,如果量比較大的話,gzip與chunked均開啟使用效果更佳哦~~~)。
圖1.2.3.1
當頁面的某些后端處理比較耗時的時候,可以試試采用分段傳輸,可以渲染一部分,就發送一部分到客戶端,雖然總時長不變,但是瀏覽器在全部傳輸完之前不會處于干等狀態。可以盡早的渲染并給予用戶反饋。
2 BIGPIPE 2.1 分段傳輸的局限剛剛筆者和讀者們一起做了分段傳輸的實驗,思路是基于讀者們想展示的網頁也是上快下慢的。可是讀者們有沒有想過,如果整個網頁中,最快的是下方,而最慢的是上方呢?這樣我們就無法利用分段傳輸的優勢了嗎?如圖2.1.1,整個頁面依舊是被最慢的第一部分數據渲染給hold住了。而后兩塊兒渲染較快,完全可以先傳輸過來。
圖2.1.1上述例子,在本文后github中的bigpipprepare文件夾中。
2.2 解決分段傳輸順序的問題看完上述描述,讀者們肯定在想,如果能把最慢的部分放置于底部傳過來就好了。于是有了一種加載思路,便是使用js回填的方式,先將左邊最慢的部分架空,然后在底部寫上js回填。這樣不就可以先渲染相對較快的右側兩塊兒了么。如圖2.2.1
后端可以先渲染快的模板,然后再渲染最慢的模板。normal1.html.php
normal2.html.php
2.normal3.html.php
3.normal4.html.php
圖2.2.1
如上圖,可以看到49ms的時候,就已經渲染出來了右側兩塊兒,2S的時候,左側也渲染出來了。上述例子,在本文后github中的bigpipe文件夾中。
2.3 回填思路的擴展與并行化我們剛剛做了一個實驗,是將耗時最慢的塊兒放在底部。然而,事實情況是,如果你也不知道哪塊兒慢了呢?或者是,你的幾塊兒數據區塊兒是并行的呢?出于剛剛的經驗,我們可以把頁面上所有的塊兒都架空,然后并行渲染,誰快誰就先渲染回填js。這樣就可以達到并行且先到先渲染的目的了。我這里做了個php并行取并回填的實驗,如圖2.3.1,可以看到,中間紅色的雖然被阻塞,但是框架先行渲染出來了所有的內容均是空的。綠色最快,先行回填渲染了出來,藍色稍慢,也跟著渲染了出來,最后紅色完畢,回填渲染結束了。
并行渲染的PHP(normal.php)主框架的模板,架空,等待回填。normal_frame.html.php
具體回填模板,normal1.html.php/normal2.html.php/normal3.html.php
圖2.3.1上述例子,在本文后github中的bigpipeparal文件夾中。
2.4 為什么不用ajax?相信讀著在此處會有疑問,為什么慢的數據,不用ajax去請求呢?這樣模板框架也能盡早的渲染出來。ajax畢竟是請求。相信很多讀著也有這樣的經歷,后端處理如果遇到了瓶頸,那么有的時候我們會選擇同步頁面渲染完之后,再發個請求去獲取后端數據。但是筆者認為,這樣做有一定弊端:
3 分段傳輸與bigpipe適用場景 3.1 分段傳輸的適用場景
1、ajax畢竟是個請求,請求就要有連接,要有解析等過程。
2、服務端和客戶端都會有閑的時候,發送ajax之前服務端閑,發送ajax出去之后,瀏覽器又閑著了。
所以,我們使用bigpipe的方式還是比多發送一個ajax有優勢的。筆者總結了一些使用分塊兒傳輸比較合適的場景
3.2 使用bigpipe的場景
1 前端需要盡早傳輸head中的一些css/js外聯文件的情況下(可以先flush給客戶端head前面的html內容,讓瀏覽器盡早的去請求)
2 后端處理渲染的數據,上方較快,下方較慢的情況(可以先行渲染上方較快的部分)對于更為復雜一點的bigpipe方式,如果上面的情況就適用于你的網站了的話,則最好采用簡單的分塊傳輸,否則如下情況,需要回填,則采用bigpipe方式渲染頁面。畢竟,使用js回填還是有性能損耗的。
3.3 國內的應用
1 后端有較慢的數據處理,阻塞住了頁面的情況下,且最慢的部分不是在網頁的最后。(可以把最慢的部分變為回填)
2 后端有多塊兒數據要并行處理的情況下(你也不知道哪塊兒先回來了,所以先渲染一個架子。對于并行的請求,先回來的先flush回填)據筆者觀察,新浪微博正是采用了bigpipe的方式進行渲染,如圖3.3.1,我們看到新浪微博的左側導航欄與中間feed流區塊兒都是架空的:
圖3.3.1
在下方,有對左側導航欄和中間feed流部分的回填,如圖3.3.2
圖3.3.2所以,整個網頁的渲染效果如下(如圖3.3.3/圖3.3.4/圖3.3.5)
4 課后作業
圖3.3.3
圖3.3.4
圖3.3.5
筆者猜測,可能微博是并行渲染這幾塊兒的數據,所以采用了bigpipe的方式。請讀者們回想一下,自己的網站到底適不適合使用分塊兒傳輸,能否使用上面的技術,使自己的網站更快一些呢?如果使用的話,是適合使用普通的chuned提速呢?還是使用bigpipe進行提速呢?
如有說明不周的地方歡迎回復詳詢
本文中所有的例子,均在我的github上可以找到:
https://github.com/houyu01/ch...接下來的一篇文章,我將會和讀者們一起聊聊HTTPS那些事兒,不要走開,請關注我.....
https://segmentfault.com/a/11...
如果喜歡本文請點擊下方的推薦哦,你的推薦會變為我繼續更文的動力。
以上內容僅代表筆者個人觀點,如有意見筆者愿意學習參考各讀者的建議。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87785.html
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼作為現代應用,的大量使用,使得前端工程師們日常的開發少不了拼裝模板,渲染模板。我們今天就來聊聊,拼裝與渲染模板的那些事兒。一改俱改,一板兩用。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼作為現代應用,的大量使用,使得前端工程師們日常的開發少不了拼裝模板,渲染模板。我們今天就來聊聊,拼裝與渲染模板的那些事兒。一改俱改,一板兩用。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼作為現代應用,的大量使用,使得前端工程師們日常的開發少不了拼裝模板,渲染模板。我們今天就來聊聊,拼裝與渲染模板的那些事兒。一改俱改,一板兩用。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼從說起要想了解,得從一個新的規則說起。因為用戶沒有安裝的話,我們強制要求顯示也沒有辦法。國內有阿里巴巴的平臺,可以選自己喜歡的圖標導出。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog/fr...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼從說起要想了解,得從一個新的規則說起。因為用戶沒有安裝的話,我們強制要求顯示也沒有辦法。國內有阿里巴巴的平臺,可以選自己喜歡的圖標導出。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog/fr...
閱讀 2064·2021-09-22 15:43
閱讀 8618·2021-09-22 15:07
閱讀 1078·2021-09-03 10:28
閱讀 2052·2021-08-19 10:57
閱讀 1061·2020-01-08 12:18
閱讀 2972·2019-08-29 15:09
閱讀 1521·2019-08-29 14:05
閱讀 1640·2019-08-29 13:57