轉(zhuǎn)轉(zhuǎn)B端項(xiàng)目頁(yè)面性能統(tǒng)計(jì)實(shí)踐
背景
由于轉(zhuǎn)轉(zhuǎn)前端業(yè)務(wù)方向主要偏向于 C 端,比如 App端內(nèi) H5、 小程序內(nèi) H5 等,并且技術(shù)棧以 Hybrid 為主(承載容器為轉(zhuǎn)轉(zhuǎn)標(biāo)準(zhǔn)化webview)。但是,近些年隨著業(yè)務(wù)不斷擴(kuò)大,逐漸出現(xiàn)了如乾數(shù)據(jù)平臺(tái)、行星平臺(tái)等 專(zhuān)門(mén)服務(wù) B 端的FE項(xiàng)目。但是沒(méi)有相關(guān)性能數(shù)據(jù)來(lái)作為參考支撐,比如需要分析用戶(hù)體驗(yàn)質(zhì)量;分析現(xiàn)有頁(yè)面性能缺陷以及后續(xù)需要做性能優(yōu)化的方向等。因此,需要一款符合轉(zhuǎn)轉(zhuǎn)內(nèi)部埋點(diǎn)上報(bào)體系的 PC 端項(xiàng)目網(wǎng)頁(yè)的性能統(tǒng)計(jì)平臺(tái)。
B 端性能統(tǒng)計(jì)面臨的問(wèn)題
由于內(nèi)部性能埋點(diǎn)統(tǒng)計(jì)體系不支持分批/分段上報(bào),每個(gè) Router 都需要作為一個(gè)單獨(dú)的頁(yè)面進(jìn)行一次性的性能數(shù)據(jù)上報(bào)。在 B 端,一些新的指標(biāo)需要支持和特殊處理。因此,在數(shù)據(jù)采集統(tǒng)計(jì)方面,我們會(huì)遇到以下幾個(gè)問(wèn)題。
- SPA Router 問(wèn)題 轉(zhuǎn)轉(zhuǎn)內(nèi)部 C 端項(xiàng)目主要采用 hybrid 技術(shù)棧,因此不需要對(duì) SPA 項(xiàng)目路由做特殊處理(因?yàn)槊看味奸_(kāi)啟一個(gè) webview,類(lèi)似于多頁(yè)面應(yīng)用應(yīng)用場(chǎng)景)。但是,基于 React 技術(shù)棧的 B 端項(xiàng)目是 SPA 項(xiàng)目,為了方便統(tǒng)計(jì)每個(gè) Router 頁(yè)面的性能數(shù)據(jù),我們需要對(duì)每個(gè) Router 頁(yè)面的加載進(jìn)行一些特殊處理。
- SPA 資源統(tǒng)計(jì)問(wèn)題 現(xiàn)在的前端 SPA 項(xiàng)目一般都會(huì)通過(guò)異步加載頁(yè)面資源的方式,進(jìn)行頁(yè)面打包體積的優(yōu)化,以提升頁(yè)面首屏性能。因此,在進(jìn)行資源統(tǒng)計(jì)時(shí),我們需要單獨(dú)對(duì)相應(yīng)的 Router 頁(yè)面的加載資源進(jìn)行統(tǒng)計(jì)處理。
- B 端指標(biāo)定義問(wèn)題 轉(zhuǎn)轉(zhuǎn) B 端性能統(tǒng)計(jì)主要參考核心指標(biāo):白屏、首屏、完全加載。頁(yè)面性能分?jǐn)?shù)評(píng)估也主要基于這三個(gè)指標(biāo)進(jìn)行加權(quán)計(jì)算。但是,在 Router 頁(yè)面加載時(shí),我們會(huì)遇到核心性能指標(biāo)無(wú)法直接獲取的問(wèn)題,因?yàn)?Router 切換并不會(huì)產(chǎn)生頁(yè)面的 load,而只是 div 的顯示隱藏。當(dāng)然了,還需要其他 B 端特有的業(yè)務(wù)標(biāo)識(shí)定義,這里不一一列舉。
主要內(nèi)容
1. 性能指標(biāo)定義
定義好哪些性能指標(biāo)需要上報(bào),是做好一個(gè)完善的采集性能數(shù)據(jù)采集 sdk 的前提條件,經(jīng)過(guò)分析主要將指標(biāo)分為兩類(lèi):1. 純 H5 頁(yè)面性能指標(biāo) 2. 頁(yè)面相關(guān)業(yè)務(wù)性指標(biāo)。
- 純 H5 頁(yè)面性能指標(biāo)
- 性能核心指標(biāo)主要包括:白屏?xí)r間 、 首屏?xí)r間、 頁(yè)面完全加載時(shí)間,以及新增的用戶(hù)體驗(yàn)指標(biāo) LCP、 FID、 CLS 。
- 輔助性性能指標(biāo)包括:DNS 解析 、請(qǐng)求響應(yīng)時(shí)間、 DOM開(kāi)始構(gòu)建時(shí)間、 頁(yè)面可交互時(shí)間、 DOM構(gòu)建完成時(shí)間、 網(wǎng)絡(luò)速度、 各類(lèi)靜態(tài)資源耗時(shí)、 ajax請(qǐng)求耗時(shí)、 LongTask 等等。
以上提到的絕大部分指標(biāo),可以通過(guò)瀏覽器提供的 PerformanceNavigationTiming PerformanceResourceTiming API 和 谷歌團(tuán)隊(duì)提供的 web-vitals 工具函數(shù)很方便的進(jìn)行獲取和計(jì)算。
- 業(yè)務(wù)性相關(guān)指標(biāo):
所謂業(yè)務(wù)性指標(biāo),主要是作為查詢(xún)分析的一些要素,比如 我們想查詢(xún)某個(gè)業(yè)務(wù)線的某個(gè)項(xiàng)目的某個(gè)頁(yè)面在某個(gè)平臺(tái)下某個(gè)性能指標(biāo)的表現(xiàn)如何?那么就需要一些非頁(yè)面性能本身的業(yè)務(wù)要素指標(biāo)進(jìn)行定義和上報(bào)統(tǒng)計(jì)。
業(yè)務(wù)指標(biāo)主要包括:actiontype 埋點(diǎn)類(lèi)型標(biāo)識(shí) 、 pagetype 業(yè)務(wù)線/項(xiàng)目標(biāo)識(shí)、pageid 頁(yè)面標(biāo)識(shí) 、 clientType 端信息、 pagestate 頁(yè)面狀態(tài)、pageurl 頁(yè)面url、 cookieid 用戶(hù)id、 fromType 來(lái)源、 loadcnt 加載次數(shù) 等等。
PS: web-vitals 由于在蘋(píng)果和低版本安卓的兼容性存在問(wèn)題,因此沒(méi)有在 C 端作為一個(gè)必選項(xiàng),但 B 端用戶(hù)絕大多數(shù)使用 chromium 內(nèi)核瀏覽器,所以大膽的將 web-vitals 納入采集指標(biāo)中
2. 指標(biāo)數(shù)據(jù)的獲取與上報(bào)
上面進(jìn)行了各種指標(biāo)的定義,那么如何高效有序的接入到轉(zhuǎn)轉(zhuǎn)埋點(diǎn)體系內(nèi)進(jìn)行上報(bào)統(tǒng)計(jì)呢?轉(zhuǎn)轉(zhuǎn)內(nèi)部其實(shí)已經(jīng)有了 C 端埋點(diǎn)體系,其實(shí)只需要按照一定的規(guī)則進(jìn)行接入即可,主要是性能平臺(tái)B端項(xiàng)目需要的字段和后端已有日志表結(jié)構(gòu)做好關(guān)系映射和擴(kuò)展。
為了解決上面提到B端項(xiàng)目的特有問(wèn)題,以及滿足上述提到所有性能指標(biāo)、業(yè)務(wù)指標(biāo)都可以很優(yōu)雅的進(jìn)行上報(bào)統(tǒng)計(jì),方便在代碼層面更好的進(jìn)行結(jié)構(gòu)上的解耦,并且盡量做到性能計(jì)算統(tǒng)計(jì)相關(guān)程序不影響頁(yè)面本身的性能,在技術(shù)實(shí)現(xiàn)設(shè)計(jì)層我們把上面的指標(biāo)做了一些分類(lèi),比如 同步計(jì)算指標(biāo)(基礎(chǔ)業(yè)務(wù)同步指標(biāo)、基礎(chǔ)性能資源同步指標(biāo))、異步計(jì)算指標(biāo)(性能異步指標(biāo)、后置異步指標(biāo))等。具體如下圖所示。
技術(shù)層面指標(biāo)分類(lèi)
下面詳細(xì)介紹一下一些關(guān)鍵邏輯是怎么處理的?各類(lèi)性能指標(biāo)具體是怎么計(jì)算的?下面列出了部分指標(biāo)怎么獲取和計(jì)算的關(guān)鍵代碼。
SPA 項(xiàng)目的路由頁(yè)面的攔截關(guān)鍵邏輯:
性能基礎(chǔ)指標(biāo)的獲取相關(guān)代碼:
資源相關(guān)指標(biāo)的數(shù)據(jù)獲取關(guān)鍵邏輯:
業(yè)務(wù)指標(biāo)數(shù)據(jù)的獲取:
longTask 的記錄獲取:
在實(shí)際項(xiàng)目統(tǒng)計(jì)時(shí),發(fā)現(xiàn)一些性能指標(biāo)算法的適用性問(wèn)題需要注意:
- LCP 算法存在的問(wèn)題。比如:觸發(fā)條件限制的問(wèn)題,當(dāng)檢測(cè)到用戶(hù)輸入時(shí)候 FMP算法會(huì)停止計(jì)算,就導(dǎo)致某些場(chǎng)景觸發(fā)不了(比如主要內(nèi)容還沒(méi)顯示就點(diǎn)擊頁(yè)面)。白屏占位圖問(wèn)題,頁(yè)面初始有較大的白屏占位圖時(shí) 即使后面被移除了,LCP 算法還會(huì)把它當(dāng)作主要內(nèi)容。
- FMP 算法不適合某些特殊場(chǎng)景。比如:2/3 是金剛位圖片布局,最下面 1/3 區(qū)域有一個(gè)瀑布流,由于FMP算法計(jì)算規(guī)則會(huì)導(dǎo)致統(tǒng)計(jì)時(shí)間在瀑布流請(qǐng)求之后展現(xiàn)后,就導(dǎo)致直觀上的頁(yè)面首屏?xí)r間變大。
數(shù)據(jù)可以計(jì)算并獲取了,那么如何進(jìn)行友好的處理上報(bào)?
由于內(nèi)部埋點(diǎn)提下不支持回話形式的分段上報(bào),那么就需要在前端提前準(zhǔn)備好所有需要需要上報(bào)的數(shù)據(jù)的處理,整體B端 SPA 項(xiàng)目性能數(shù)據(jù)處理的上報(bào)處理機(jī)制,以及同步任務(wù)數(shù)據(jù)、異步任務(wù)數(shù)據(jù)任務(wù)的處理流如下圖所示。
3. 上報(bào)數(shù)據(jù)的體積優(yōu)化
在進(jìn)行數(shù)據(jù)上報(bào)時(shí),如果頁(yè)面的靜態(tài)資源加載 / ajax請(qǐng)求數(shù)量很多時(shí),埋點(diǎn)上報(bào)請(qǐng)求接口的 body 會(huì)很大,導(dǎo)致請(qǐng)求耗時(shí)長(zhǎng)而影響頁(yè)面本身的性能。因此針對(duì) body 過(guò)大的問(wèn)題,對(duì)一些資源的統(tǒng)計(jì)做了序列化處理。
比如:?jiǎn)螚l靜態(tài)資源的原始數(shù)據(jù)結(jié)構(gòu)為:
序列化之后,將各個(gè)關(guān)鍵數(shù)據(jù)合并成一個(gè)字符串,即:
可以發(fā)現(xiàn)系列化精簡(jiǎn)后將 255個(gè)字符優(yōu)化成了 42 個(gè)字符。
往往B端 SPA 項(xiàng)目靜態(tài)資源和請(qǐng)求多達(dá)幾十上百個(gè),這樣序列化處理合并之后,能將埋點(diǎn)上報(bào)請(qǐng)求 body 體積減少數(shù)千個(gè)字節(jié)。當(dāng)然了,如果服務(wù)支持編解碼,還可以通過(guò)其他更優(yōu)的序列化方案進(jìn)行 body 體積壓縮。
4. 數(shù)據(jù)存儲(chǔ)與處理
在對(duì)數(shù)據(jù)進(jìn)行處理時(shí),也遇到了一些問(wèn)題。
每天上報(bào)的性能埋點(diǎn)數(shù)據(jù)存儲(chǔ)在哪里?
如何計(jì)算數(shù)據(jù)?如何擴(kuò)展數(shù)據(jù)?如何查詢(xún)數(shù)據(jù)?
二次計(jì)算后的數(shù)據(jù)量依舊非常大,該怎么辦?
- 原始性能數(shù)據(jù)通過(guò)SDK采集后,經(jīng)過(guò)數(shù)據(jù)倉(cāng)庫(kù)的清洗,存儲(chǔ)在Hadoop中。雖然Hadoop可以存儲(chǔ)PB級(jí)別的數(shù)據(jù),但查詢(xún)速度較慢,不適合實(shí)時(shí)性能分析查詢(xún)。為了解決這個(gè)問(wèn)題,我們嘗試將清洗后的數(shù)據(jù)復(fù)制一份存儲(chǔ)在MySQL中,但隨著數(shù)據(jù)量的增加,MySQL方案出現(xiàn)了許多問(wèn)題。考慮到實(shí)際場(chǎng)景的并發(fā)量不會(huì)太高,我們最終選擇將明細(xì)數(shù)據(jù)存儲(chǔ)在ClickHouse中。
- 雖然明細(xì)數(shù)據(jù)可以通過(guò)查詢(xún)ClickHouse獲取,但對(duì)于許多聚合計(jì)算得出的數(shù)據(jù),如果仍然通過(guò)查詢(xún)并實(shí)時(shí)計(jì)算,效率并不理想。因此,在數(shù)據(jù)落庫(kù)后,我們通過(guò)定時(shí)任務(wù)預(yù)先計(jì)算一部分聚合數(shù)據(jù),然后將其導(dǎo)入MySQL中。這種做法的好處在于,這部分預(yù)先計(jì)算好的數(shù)據(jù)可以進(jìn)行查詢(xún),用戶(hù)體驗(yàn)更好,而且后續(xù)需要擴(kuò)展時(shí),只需要對(duì)聚合數(shù)據(jù)進(jìn)行二次計(jì)算加工即可。目前,聚合數(shù)據(jù)使用了6個(gè)維度進(jìn)行分組計(jì)算,這些維度也可以用于組合查詢(xún),方便后續(xù)擴(kuò)展。
- 盡管聚合數(shù)據(jù)已經(jīng)合并計(jì)算過(guò),但由于多維度組合,數(shù)據(jù)量仍然非常龐大。隨著后續(xù)維度的擴(kuò)展,整體數(shù)據(jù)量呈指數(shù)級(jí)增長(zhǎng)。考慮到性能分析的周期性不會(huì)太長(zhǎng),我們決定只保留整體聚合數(shù)據(jù)7天,并進(jìn)行分表處理。如果數(shù)據(jù)量激增,我們會(huì)采取將數(shù)據(jù)轉(zhuǎn)入TiDB中,并按日期進(jìn)行分區(qū)存儲(chǔ)。
為了解決數(shù)據(jù)處理中的兩個(gè)核心問(wèn)題,我們采用了這個(gè)完整的流程。在面對(duì)如此龐大的數(shù)據(jù)時(shí),我們需要考慮它們存儲(chǔ)在何處。同時(shí),我們也需要考慮如何查找和計(jì)算需要的指標(biāo)。這個(gè)流程可以幫助我們更好地處理數(shù)據(jù),提高效率。
此外,這個(gè)流程還有一個(gè)重要的作用,那就是保證數(shù)據(jù)的準(zhǔn)確性和完整性。在數(shù)據(jù)處理過(guò)程中,我們需要遵循一定的規(guī)則和標(biāo)準(zhǔn),以確保數(shù)據(jù)的可靠性。這樣才能讓我們?cè)诜治鰯?shù)據(jù)時(shí)得出正確的結(jié)論,更好的進(jìn)行針對(duì)性的優(yōu)化。
5. 性能查詢(xún)展示平臺(tái)
web平臺(tái)部分功能頁(yè)面展示如下:
歷史變化曲線
性能數(shù)據(jù)查詢(xún)
總結(jié)
在B端項(xiàng)目中,頁(yè)面性能統(tǒng)計(jì)是非常有必要的,因?yàn)榭梢詭椭覀兞私鈱?shí)際用戶(hù)的具體頁(yè)面的加載速度、用戶(hù)體驗(yàn),以便了解當(dāng)前頁(yè)面的質(zhì)量,并且為優(yōu)化頁(yè)面性能提供方向,從而提高用戶(hù)滿意度。