成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

騰訊祭出大招VasSonic,讓你的H5頁面首屏秒開

xzavier / 1718人閱讀

摘要:經(jīng)過一系列優(yōu)化后,在平臺上,點擊到頁面首屏展示的耗時從平均多降低為,優(yōu)化以上。而現(xiàn)在頁面為了更好地為用戶推薦喜歡的內(nèi)容,我們后臺引入機器學(xué)習(xí)和隨機算法來做智能個性化推薦。另外還有部分的內(nèi)容是隨機算法推薦的。

VasSonic成長歷程
前言

2017.8.8 14時,SNG增值產(chǎn)品部Vas團隊研發(fā)的輕量級高性能Hybrid框架VasSonic通過了公司最終審核,作為騰訊開源組件分享給大家。從當(dāng)初立項優(yōu)化頁面加載速度,到不斷摸索、優(yōu)化,再到整理代碼、文檔,最終在Github上開源,并且在24小時內(nèi)獲取star數(shù)超過1600。我們非常高興看到我們的成果收到這么多的關(guān)注,趁此機會,正好回顧一下VasSonic的成長歷程,也希望能夠讓大家更了解VasSonic。

項目背景

Web相信大家再熟悉不過了,它具有快速迭代發(fā)布的天然優(yōu)勢,但也存在中一些讓人詬病的問題,比如加載速度慢,體驗差等。在此之前,手Q上很多頁面首屏打開速度居高不下,甚至有些耗時達(dá)到3s以上,這意味著用戶打開頁面必須經(jīng)過3秒之后才能進行交互操作,體驗相當(dāng)差,很多用戶忍受不了這個漫長的時間直接流失掉了。

為了提升用戶體驗和業(yè)務(wù)用戶留存率,我們很多業(yè)務(wù)一開始通過Web開發(fā),等頁面模型驗證符合預(yù)期后,再將H5頁面轉(zhuǎn)化成原生界面。我們很快意識到這不是一種健康的可持續(xù)的開發(fā)模式,一方面存在重復(fù)人力浪費,另外一方面原生商城除了速度快一點,要運營活動改版都很難。

所以后來團隊改了切入方向,安排人力專心研究如何加快頁面打開速度,經(jīng)過了一系列的摸爬滾打和優(yōu)化探索,最終我們研發(fā)出了VasSonic框架,讓H5頁面首屏達(dá)到秒開,給用戶一個更好的H5體驗。下面就和大家分享VasSonic框架的發(fā)展歷程。

業(yè)務(wù)形態(tài)

任何一個技術(shù)框架都是結(jié)合具體的業(yè)務(wù)形態(tài)來進行發(fā)展優(yōu)化的,技術(shù)是為了更好地服務(wù)業(yè)務(wù),業(yè)務(wù)也會驅(qū)動技術(shù)的發(fā)展。在此首先介紹一下業(yè)務(wù)形態(tài),我們是來自手Q增值產(chǎn)品部門的VAS團隊,負(fù)責(zé)手機QQ上很多深受年輕人喜歡的個性化增值服務(wù),比如氣泡、掛件、主題等等。手Q上大部分的業(yè)務(wù)還是基于H5開發(fā)的,大家對手Q的業(yè)務(wù)形態(tài)可能有簡單的了解。比如下圖的游戲分發(fā)中心、會員特權(quán)中心、個性化裝扮商城等。這部分商城的特點比較明顯,頁面的很多數(shù)據(jù)都是動態(tài)的,是由我們的產(chǎn)品經(jīng)理在后臺配置的。

這些都是很常見頁面,我們通常將html/js/css等靜態(tài)資源放到CDN上,然后頁面加載后,再通過CGI去拉取最新的數(shù)據(jù),進行拼接展示, 這樣子可以利用到CDN的多地部署和就近接入等優(yōu)勢,同時提高了服務(wù)器的并發(fā)能力。這種傳統(tǒng)模式的加載流程如下所示:

用戶點擊后,經(jīng)過終端一系列初始化流程,比如進程啟動、Runtime初始化、創(chuàng)建WebView等等。

完成初始化后,WebView開始去CDN上面請求Html加載頁面。

頁面發(fā)起CGI請求對應(yīng)的數(shù)據(jù)或者通過localStorage獲取數(shù)據(jù),數(shù)據(jù)回來后再對DOM進行操作更新

可以看出上述流程存在著幾個問題:

從外網(wǎng)統(tǒng)計數(shù)據(jù)來看,用戶的終端耗時在1s以上,這意味著在這1s多的時間里,網(wǎng)絡(luò)完全是空閑在等待的,非常浪費;

頁面的資源和數(shù)據(jù)完全依賴于網(wǎng)絡(luò),特別是用戶在弱網(wǎng)絡(luò)場景下,頁面會出現(xiàn)很長時間的白屏,體驗非常差;

因為頁面的數(shù)據(jù)依賴于動態(tài)拉取,加載完頁面后,往往是看到一些模塊先轉(zhuǎn)菊花,再展示,體驗也是不好的。同時這里涉及到較多數(shù)據(jù)更新,經(jīng)常要更新DOM,性能上也有不少開銷。

所以針對以上幾個問題,我們也對應(yīng)做了很多優(yōu)化和探索。

VasSonic的前世 優(yōu)化終端

針對終端耗時1s以上的情況,我們對手Q WebView框架進行了重構(gòu):

啟動流程徹底拆分,設(shè)計為一個狀態(tài)機按序按需執(zhí)行

View相關(guān)拆分模塊化設(shè)計,盡可能懶加載,IO異步化

X5內(nèi)核在手Q中的獨立進程中提前預(yù)加載

創(chuàng)建WebView對象復(fù)用池

關(guān)于第四點,我們想分享一些Android平臺上的細(xì)節(jié),由于Android系統(tǒng)的生態(tài)原因,導(dǎo)致用戶的系統(tǒng)版本和系統(tǒng)Webkit內(nèi)核處于極其分裂狀態(tài),所以我們公司在手Q和微信統(tǒng)一使用X5內(nèi)核。相對系統(tǒng)WebView來說,首次啟動X5內(nèi)核時,創(chuàng)建WebView比較耗時,因此我們盡量想復(fù)用WebView,但是WebView卻是與Activity Context綁定。銷毀復(fù)用的時候,需要釋放Activity的Context,否則會內(nèi)存泄露。針對這種情況,有沒有一種兩全其美的辦法呢?

計算機有一句經(jīng)典的名言:計算機領(lǐng)域任何一個問題都可以通過引入中間層來解決。于是我們通過包裝的方式,實現(xiàn)了一個Context的殼,真正的實現(xiàn)體包裝在里面,邏輯調(diào)用真正調(diào)用到對應(yīng)的實現(xiàn)體的函數(shù)。 經(jīng)過實驗發(fā)現(xiàn),Android系統(tǒng)本身提供了這么一個MutableContextWrapper,作為Context的一個中間層。

我們會將Activity context包在MutableContextWrapper里面,destory的時候,會將WebView的Context設(shè)置為Application的Context,從而釋放Activity Context。
類似如下:

//precreate WebView
MutableContextWrapper contextWrapper = new MutableContextWrapper(BaseApplicationImpl.sApplication);
mPool[0] = new WebView(contextWrapper);

//reset WebView 
ct =(MutableContextWrapper)webview.getContext();
ct.setBaseContext(getApplication());

//reuse WebView
((MutableContextWrapper)webview.getContext()).setBaseContext(activityContext);
靜態(tài)直出

“直出”這個概念對前端同學(xué)來說,并不陌生。為了優(yōu)化首屏體驗,大部分主流的頁面都會在服務(wù)器端拉取首屏數(shù)據(jù)后通過NodeJs進行渲染,然后生成一個包含了首屏數(shù)據(jù)的Html文件,這樣子展示首屏的時候,就可以解決內(nèi)容轉(zhuǎn)菊花的問題了。
當(dāng)然這種頁面“直出”的方式也會帶來一個問題,服務(wù)器需要拉取首屏數(shù)據(jù),意味著服務(wù)端處理耗時增加。
不過因為現(xiàn)在Html都會發(fā)布到CDN上,WebView直接從CDN上面獲取,這塊耗時沒有對用戶造成影響。
手Q里面有一套自動化的構(gòu)建系統(tǒng)Vnues,當(dāng)產(chǎn)品經(jīng)理修改數(shù)據(jù)發(fā)布后,可以一鍵啟動構(gòu)建任務(wù),Vnues系統(tǒng)就會自動同步最新的代碼和數(shù)據(jù),然后生成新的含首屏Html,并發(fā)布到CDN上面去。

離線預(yù)推

頁面發(fā)布到CDN上面去后,那么WebView需要發(fā)起網(wǎng)絡(luò)請求去拉取。當(dāng)用戶在弱網(wǎng)絡(luò)或者網(wǎng)速比較差的環(huán)境下,這個加載時間會很長。于是我們通過離線預(yù)推的方式,把頁面的資源提前拉取到本地,當(dāng)用戶加載資源的時候,相當(dāng)于從本地加載,即使沒有網(wǎng)絡(luò),也能展示首屏頁面。這個也就是大家熟悉的離線包。
手Q使用7Z生成離線包, 同時離線包服務(wù)器將新的離線包跟業(yè)務(wù)對應(yīng)的歷史離線包進行BsDiff做二進制差分,生成增量包,進一步降低下載離線包時的帶寬成本,下載所消耗的流量從一個完整的離線包(253KB)降低為一個增量包(3KB)。

經(jīng)過一系列優(yōu)化后,在Android平臺上,點擊到頁面首屏展示的耗時從平均3s多降低為1.8s,優(yōu)化40% 以上。

VasSonic的誕生

雖然通過靜態(tài)直出和離線預(yù)推等方式優(yōu)化后,速度已經(jīng)達(dá)到1.8s,但還存在很大的優(yōu)化空間,當(dāng)我們準(zhǔn)備持續(xù)深入優(yōu)化時,我們的業(yè)務(wù)形態(tài)發(fā)生了新的變化。

之前我們頁面內(nèi)容的數(shù)據(jù)主要是由產(chǎn)品經(jīng)理要配置的,用戶看到的內(nèi)容基本都是一樣的。而現(xiàn)在頁面為了更好地為用戶推薦喜歡的內(nèi)容,我們后臺引入機器學(xué)習(xí)和隨機算法來做智能個性化推薦。比如左邊新用戶推薦的是新貨精選,而右邊活躍用戶展示的是潮品推薦。另外還有部分的內(nèi)容是隨機算法推薦的。這意味著不同用戶看到的內(nèi)容是不同的,同一個用戶不同時間看到的內(nèi)容也有可能不同。

所以為了滿足業(yè)務(wù)的需求,我們只能實時拉取用戶數(shù)據(jù)并在服務(wù)端渲染后返回給客戶端,也就是動態(tài)直出的方案。

但是動態(tài)直出方案存在幾個比較明顯的問題:

服務(wù)端實時拉取數(shù)據(jù)渲染導(dǎo)致白屏?xí)r間長,因為服務(wù)器要先實時拉取個人數(shù)據(jù),然后進行渲染直出,這個耗時不可控;

首屏無法使用離線預(yù)推等緩存策略,因為每個用戶看到的內(nèi)容不一樣,我們無法通過靜態(tài)直出的方式那樣把Html全部發(fā)布到CDN;

雖然動態(tài)直出方案下,頁面首屏無法通過離線預(yù)推等方式進行加載優(yōu)化,但前面優(yōu)化積累的經(jīng)驗給我們提供了思路:要優(yōu)化白屏問題,核心還是得從提升資源加載速度方向入手。所以我們重點在資源加載方面進行了深度優(yōu)化。

并行加載

首先在加載流程方面,我們發(fā)現(xiàn)這里WebView訪問依然是串行的, WebView要等終端初始化完成之后,才發(fā)起請求。雖然終端耗時優(yōu)化了不少,但是從外網(wǎng)的統(tǒng)計數(shù)據(jù)來看,終端初始化還是存在幾百毫秒的耗時,而這段時間內(nèi)網(wǎng)絡(luò)是在空等的。

因此性能上不夠極致,我們優(yōu)化代碼,這兩個操作并行處理,流程改為:

并行處理后速度有所改善,但我們發(fā)現(xiàn)在某些場景下,終端初始化比較快,但數(shù)據(jù)沒有完成返回,這意味著內(nèi)核在空等,而內(nèi)核是支持邊加載邊渲染的,我們在并行的同時,能否也利用內(nèi)核的這個特性呢?

于是我們加入了一個中間層來橋接內(nèi)核和數(shù)據(jù),內(nèi)部稱為流式攔截:

啟動子線程請求頁面主資源,子線程中不斷講網(wǎng)絡(luò)數(shù)據(jù)讀取到內(nèi)存中,也就是網(wǎng)絡(luò)流(NetStream)和內(nèi)存流(MemStream)之間的轉(zhuǎn)換;

當(dāng)WebView初始化完成的時候,提供一個中間層BridgeStream來連接WebView和數(shù)據(jù)流;

當(dāng)WebView讀取數(shù)據(jù)的時候,中間層BridgeStream會先把內(nèi)存的數(shù)據(jù)讀取返回后,再繼續(xù)讀取網(wǎng)絡(luò)的數(shù)據(jù)。

通過這種橋接流的方式,整個內(nèi)核無需等待,繼續(xù)做到邊加載邊解析。這種并行的方式讓首屏的速度優(yōu)化15%以上,進一步提升了頁面加載速度。

動態(tài)緩存

通過并行加載,我們極大地提升了WebView請求的速度,但是在弱網(wǎng)絡(luò)場景下白屏?xí)r間還是非常長,用戶體驗非常糟糕。于是我們在思考,是否能夠?qū)⒂脩舻囊呀?jīng)加載的頁面內(nèi)容緩存下來,等用戶下此點擊頁面的時候,我們先加載展示頁面緩存,第一時間讓用戶看到內(nèi)容,然后同時去請求新的頁面數(shù)據(jù),等新的頁面數(shù)據(jù)拉取下來之后,我們再重新加載一遍即可。

保存頁面內(nèi)容這個工作很簡單,因為現(xiàn)在我們資源讀取都是通過中間層BridgeStream來管理的,只需要將整個讀取的內(nèi)容緩存下來即可。
于是我們就按動態(tài)緩存這種方案去實現(xiàn)了,但很快就發(fā)現(xiàn)了問題。用戶打開頁面之后,先是看到歷史頁面,等用戶準(zhǔn)備去操作的時候,突然頁面白閃一下,重新加載了一遍,這種體驗非常差,特別在一些低端機器上,這個白閃的過程太明顯,非常影響體驗,這是用戶和產(chǎn)品經(jīng)理都不能接受的。于是我們在思考,能否只做局部的刷新,僅刷新變化的元素呢?

通過分析,我們發(fā)現(xiàn)同一個用戶的頁面,大部分?jǐn)?shù)據(jù)都是不變的,經(jīng)常變化的只有少量數(shù)據(jù),于是我們提出了模板(template)和數(shù)據(jù)塊(data)的概念:頁面中經(jīng)常變化的數(shù)據(jù)我們稱為數(shù)據(jù)塊,除了數(shù)據(jù)塊之外的數(shù)據(jù)稱為模板。

頁面分離

我們將整個頁面html通過VasSonic標(biāo)簽進行劃分,包裹在標(biāo)簽中的內(nèi)容為data,標(biāo)簽外的內(nèi)容為模版。

首先我們對Html內(nèi)容進行了擴展,通過代碼注釋的方式,增加了“sonicdiff-xxx”來標(biāo)注一個數(shù)據(jù)塊的開始與結(jié)束。
而模板就是將數(shù)據(jù)塊摳掉之后的Html,然后通過{albums}來表示這個是一個數(shù)據(jù)塊占位。
數(shù)據(jù)就是JSON格式,直接Key-Value。
當(dāng)然,為了完美地兼容Html,我們對協(xié)議頭部進行了擴展,比如增加accept-diff來標(biāo)注是否支持增量更新、template-tag來標(biāo)注模板的md5是多少等。OK,有了上面這個規(guī)則或者公式后,我們就可以實現(xiàn)增量更新了。

請求規(guī)范約定

VasSonic為了支持區(qū)分客戶端是否支持增量更新等能力,對頭部字段進行了擴展

字段 說明 請求頭(Y/N) 響應(yīng)頭(Y/N)
accept-diff 表示終端是否支持VasSonic模式,true為支持,否則不支持 Y N
If-none-match 本地緩存的etag,給服務(wù)端判斷是否命中304 Y N
etag 頁面內(nèi)容的唯一標(biāo)識(哈希值) N Y
template-tag 模版唯一標(biāo)識(哈希值),客戶端使用本地校驗 或 服務(wù)端使用判斷是模板有變更 Y Y
template-change 標(biāo)記模版是否變更,客戶端使用 N Y
cache-offline 客戶端端使用,根據(jù)不同類型進行不同行為 N Y
cache-offline字段說明
字段 說明
true 緩存到磁盤并展示返回內(nèi)容
false 展示返回內(nèi)容,無需緩存到磁盤
store 緩存到磁盤,如果已經(jīng)加載緩存,則下次加載,否則展示返回內(nèi)容
http 容災(zāi)字段,如果http表示終端六個小時之內(nèi)不會采用sonic請求該URL
模式介紹

VasSonic根據(jù)本地是否有緩存以及本地緩存數(shù)據(jù)跟服務(wù)器數(shù)據(jù)的差異情況分為以下四種模式。

模式 說明 條件
首次加載 本地沒有緩存,即第一次加載頁面 etag為空值或template_tag為空值
完全緩存 本地有緩存,且緩存內(nèi)容跟服務(wù)器內(nèi)容完全一樣 etag一致
數(shù)據(jù)更新 本地有緩存,本地模版內(nèi)容跟服務(wù)器模版內(nèi)容一樣,但數(shù)據(jù)塊有變化 etag不一致 且 template_tag一致
模版更新 本地有緩存,緩存的模版內(nèi)容跟服務(wù)器的模版內(nèi)容不一樣 etag不一致 且 template_tag不一致
首次加載

我們會在請求頭部帶上支持accept-diff為true和sdk版本號等標(biāo)識著首次加載的信息。當(dāng)請求返回后,VasSonic會在延遲幾秒后(避免激烈IO競爭)將頁面抽離成模板和數(shù)據(jù)并保存到本地。此時終端緩存目錄下,該頁面將對應(yīng)三個緩存文件xxx.html、xxx.template、xxx.data,其中xxx是該頁面的唯一標(biāo)識(即sonicSessionId)。

對于頁面非首次加載場景,VasSonic優(yōu)先加載本地緩存, 同時我們會在請求頭部帶上當(dāng)前緩存和模板的md5,后臺進行模板md5對比之后,分為以下幾種情況:

非首次加載之完全緩存

本地有緩存,且緩存內(nèi)容跟服務(wù)器內(nèi)容完全一樣.

非首次加載之增量數(shù)據(jù)

如果模板發(fā)現(xiàn)沒有變化,那么會在響應(yīng)頭部返回template-change=false,同時響應(yīng)包體返回的數(shù)據(jù)不再是完整的html,而是一段JSON數(shù)據(jù),及全部的數(shù)據(jù)塊。我們現(xiàn)在需要跟本地數(shù)據(jù)進行差分,找出真正的增量數(shù)據(jù),如上圖中,后臺返回了N個數(shù)據(jù),實際上僅有一個數(shù)據(jù)是有變化的,那么我們僅需要將這個變化的數(shù)據(jù)提交到頁面即可。一般場景下,這個差異的數(shù)據(jù)比全部數(shù)據(jù)要小很多。如果頁面拆分?jǐn)?shù)據(jù)得更細(xì),那么頁面的變動就更小,這個取決于前端同學(xué)對數(shù)據(jù)塊的細(xì)化程度。

獲得變化數(shù)據(jù)塊(diff_data)后,客戶端只需要通知頁面頁面設(shè)置的回調(diào)接口(getDiffDataCallback)進行界面元素更新即可。這里javascript的通信方式也可以自由定義(可以使用webview標(biāo)準(zhǔn)的javascript通信方式,也可以使用偽協(xié)議的方式),只要頁面跟終端協(xié)商一致就可以。

對于數(shù)據(jù)更新這種場景,終端還會將新的數(shù)據(jù)和模板拼接成為新的頁面,保持緩存最新。當(dāng)終端初始化比較慢的時候,WebView去加載緩存的時候,這個頁面可能已經(jīng)是最新的了,連數(shù)據(jù)刷新都不需要。

非首次加載之模板更新

與數(shù)據(jù)更新模式不一樣,由于業(yè)務(wù)需求,頁面的模板會發(fā)生更改。當(dāng)終端在獲取到新的模板和數(shù)據(jù)后,本地在子線程中進行合并,生成一個新的緩存,然后回調(diào)通知終端,刷新WebView來加載新的緩存。

我們來看一下最終的流程圖,跟動態(tài)緩存對比,有不少細(xì)節(jié)優(yōu)化:

我們從第2步開始,SonicSession首先會去讀取緩存。會拋個消息通知WebView讀取緩存,如果Webview已經(jīng)準(zhǔn)備好,則直接加載緩存,如果沒有,則緩存先放在內(nèi)存里面。同時SonicSession也會帶上模板等信息到后臺拉取新的內(nèi)容,后臺經(jīng)過Sonic-Diff之后,會返回新的數(shù)據(jù)。SonicSession拿到新的數(shù)據(jù)后,首先會跟本地數(shù)據(jù)進行Diff,如果發(fā)現(xiàn)WebView已經(jīng)加載緩存,則直接提交增量數(shù)據(jù)給頁面。否則繼續(xù)拼接最新的頁面,替換掉內(nèi)存里面的緩存,同時保存到本地。這個時候WebView如果Ready,則直接進行第5步load最新的內(nèi)容即可。

效果統(tǒng)計

這個是我們外網(wǎng)的統(tǒng)計數(shù)據(jù)。在數(shù)據(jù)更新模式下,首屏的耗時在1s左右,相比普通的動態(tài)直出,優(yōu)化了50%以上。模板更新這個會比首次高,是因為加載了兩次頁面,不過從模式占比上來看,我們大部分頁面都是數(shù)據(jù)更新。針對模板更新這種耗時比較高的情況,前面優(yōu)化積累的經(jīng)驗給我們提供了思路,核心還是從提前獲取資源方向入手,因此我們優(yōu)先考慮如何預(yù)加載模板更新。

預(yù)加載

實際上整個SonicSession在沒有WebView的情況下,也是可以獨立完成所有邏輯的,當(dāng)用戶點擊頁面的時候,我們在將WebView和SonicSession綁定起來即可。于是我們支持了兩種預(yù)加載的模式,一種是通過后臺push的方式,來提前獲取數(shù)據(jù)。還有一種就是JSAPI,頁面可以調(diào)用JSAPI來預(yù)加載用戶可能操作的下一個頁面。通過這兩種方式,我們可以把需要的增量更新數(shù)據(jù)提前拉取回來

效果對比
Pic 1: 沒有使用VasSonic Pic 2: 使用VasSonic
展望未來

開源只是故事的開始,我們?nèi)詴掷m(xù)對 VasSonic 做改進,包括更易用的接口、更好的性能、更高的可靠性,同時快速響應(yīng)解決開源后的issue和PR。這些改進最終也會原封不動地在手Q內(nèi)使用,這一切都是為了更快的WebView加載速度。

Talk is cheap,read the fucking code. If you are interested in VasSonic, don"t forget to STAR VasSonic.
Thank you for reading ~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/51132.html

相關(guān)文章

  • 2017-08-18 前端日報

    2017-08-18 前端日報 精選 [譯] 關(guān)于 React Router 4 的一切騰訊祭出大招VasSonic,讓你的H5頁面首屏秒開用簡單的方法學(xué)習(xí)ECMAScript 6【譯】一個小時搭建一個全棧Web應(yīng)用框架你應(yīng)該知道的Debug技巧Understanding V8’s Bytecode – DailyJS – MediumAnnouncing TypeScript 2.5 RC 中文...

    AZmake 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<