汽車之家頁面性能監(jiān)控建設(shè)實踐
一、前言
關(guān)注用戶體驗,提高頁面性能,是每位前端研發(fā)同學(xué)的日常工作之一。提高頁面性能對業(yè)務(wù)的幫助,雖不易衡量,但肯定是利遠大于弊。如何衡量頁面性能優(yōu)劣?如何幫助研發(fā)同學(xué)快速定位到頁面性能瓶頸點?一直是前端的重點工作之一。本文分享汽車之家在頁面性能監(jiān)控建設(shè)方面的部分工作,主要包含三方面:
技術(shù)選型
- 該選擇哪些頁面性能監(jiān)控技術(shù)方案?
- 在盡可能不影響頁面性能的前提下,既能客觀、全面衡量頁面性能,又能幫助研發(fā)同學(xué)快速定位性能瓶頸點,該采集哪些指標(biāo)?
- SPA 應(yīng)用非首頁性能該如何評估?
- 在盡可能不影響頁面性能和保證采集數(shù)據(jù)精準(zhǔn)性的前提下,盡量多地采集和上報數(shù)據(jù),如何選擇合適的指標(biāo)采集時機和上報方式?
整體架構(gòu)設(shè)計
整合選中技術(shù)方案,構(gòu)建體系化性能監(jiān)控架構(gòu),提供性能監(jiān)控和性能分析工具鏈,支持產(chǎn)研同學(xué)在 DevOps 各階段中發(fā)現(xiàn)和定位頁面性能問題。
建立評判體系
有數(shù)據(jù),我們才能度量;有評分,技術(shù)才好改進。
利用采集到的眾多指標(biāo),根據(jù)應(yīng)用特性,按各性能指標(biāo)的重要程度,設(shè)置不同的基線和權(quán)重,以加權(quán)平均的方式,求得應(yīng)用得分。通過分?jǐn)?shù),直觀告訴研發(fā)同學(xué)應(yīng)用頁面快或慢?應(yīng)用性能高或低?是否需要改進?
應(yīng)用得分只能反映單個應(yīng)用的性能情況,主要服務(wù)于產(chǎn)研同學(xué)。一家公司有多個部門,每個部門有多個團隊,一個團隊有多個應(yīng)用,我們需要公司、部門和團隊層級的性能得分,才能讓各級領(lǐng)導(dǎo)直觀了解其負責(zé)隊伍的頁面性能,也方便上級領(lǐng)導(dǎo)判斷下級各隊伍之間的性能高低,所以我們根據(jù)應(yīng)用 PV 數(shù)和應(yīng)用級別,仍以加權(quán)平均算法,獲得團隊、部門和公司性能得分。
二、技術(shù)選型
根據(jù)監(jiān)控頁面性能時的運行環(huán)境,我們將技術(shù)方案分為兩種:合成監(jiān)控(Synthetic Monitoring,SYN)和真實用戶監(jiān)控(Real User Monitoring,RUM)。
合成監(jiān)控 (以下簡稱 SYN)
指在通過仿真環(huán)境運行頁面,評估頁面性能。早期代表工具有我們熟知的 YSlow 和 PageSpeed。隨著技術(shù)進步,當(dāng)前三個最成熟的 SYN 工具為:Lighthouse、WebPageTest 和 SiteSpeed。Lighthouse 雖然僅支持 Chrome 瀏覽器、實施成本較高,但是有谷歌支持、易擴展、指標(biāo)豐富、有評分諸多優(yōu)勢,已逐步代替 WebPageTest,成為 SYN 首選工具。 如下以 Lighthouse 為例介紹 SYN 的運行過程、優(yōu)缺點。
運行過程
從運行結(jié)果頁面來看,Lighthouse 除了輸出關(guān)鍵性能指標(biāo)值和評分外,還向我們提供優(yōu)化建議和診斷結(jié)果。10.1.0 版 Lighthouse 分別內(nèi)置 94、16 條性能和最佳實踐方面的規(guī)范或建議,其中不乏日常研發(fā)比少留意且較有意義的規(guī)范,如:最大限度地減少主線程工作(mainthread-work-breakdown)、網(wǎng)頁已阻止恢復(fù)往返緩存(bf-cache)、減少 js 文件中未使用的 JavaScript (unused-javascript)等。
推薦使用 Node Cli 或 Node Module 方式運行 Lighthouse,同時輸出 html 和 json 格式的結(jié)果。json 中數(shù)據(jù)更全面,包含如:最大內(nèi)容渲染時間元素(largest-contentful-paint-element)、應(yīng)避免出現(xiàn)長時間運行的主線程任務(wù)(long-tasks)等明細信息。
優(yōu)缺點
根據(jù)我們實踐,總結(jié) Lighthouse 有如下優(yōu)缺點:
改進
針對上述不足和產(chǎn)品需求,我們做了一些改進:
n 為解決 默認(rèn)無基準(zhǔn)環(huán)境,相同頁面在不同用戶端運行,因運行環(huán) 境和硬件資源不同,導(dǎo)致結(jié)果不同 的問題,我們做了兩方面的改進:
首先,提供 SYN 基準(zhǔn)運行環(huán)境。利用 Lighthouse Node Module 自研 Web 版 SYN 服務(wù)并部署在容器中。通過在 Node 服務(wù)端添加隊列策略,保證單容器任意時間只允許運行一個 SYN 任務(wù),且每個容器的硬件資源( 4 核 + 4G )和網(wǎng)速配置( M 端應(yīng)用統(tǒng)一使用 10M 網(wǎng)速 )都一樣,從而保證運行結(jié)果和最終得分是相對公平和可靠的。
其次,支持以計劃任務(wù),間隔 6、12 或 24 小時 的方式運行 SYN 任務(wù)。 統(tǒng)計多次運行結(jié)果指標(biāo)的 AVG、TP 值,排除少數(shù)異常運行的結(jié)果偏差。
- 針對 運行慢,占用資源多 的問題。我們認(rèn)為相同頁面,如果沒有改版,沒必要過于頻繁的持續(xù)測試,建議 PV 量大或重要頁面添加計劃任務(wù) 12 小時及以上時間間隔運行一次,這樣既能客觀反映頁面性能情況,也能節(jié)省資源。
- 將 SYN 集成到 CI 流水線中,將 SYN 作為上線前頁面性能測試或競品對比的實施工具。
適用場景
- 將 SYN 作為頁面性能測試工具,集成到前端監(jiān)控后臺應(yīng)用、QA 套件 和 CI 中。建議研發(fā)同學(xué)交付頁面前,通過 SYN 評估頁面性能并根據(jù)優(yōu)化建議和診斷結(jié)果,改進頁面質(zhì)量缺陷,提高交付質(zhì)量。
- 利用 SYN 做競品對比。
- 將 SYN 作為分析 RUM 捕獲到慢頁面的首選工具,結(jié)合 Chrome DevTools , 從實踐來看可以定位到多數(shù)問題。
使用方法
總結(jié)
SYN 實施成本低,便于統(tǒng)一標(biāo)準(zhǔn),相比 RUM,受運行時環(huán)境的影響更小,結(jié)果更具有可比性和可復(fù)現(xiàn)性,是性能監(jiān)控的重要一環(huán)。基于 Lighthouse,借助 K8S 等容器編排技術(shù),快速搭建提供基準(zhǔn)環(huán)境的 SYN Web 服務(wù)是建設(shè)頁面性能監(jiān)控體系的第一階段。該階段以提供評估頁面性能、分析慢頁面等關(guān)鍵功能為主。雖然 Lighthouse 還存在 僅支持谷歌瀏覽器,代表性不足,也不能真實反映真實用戶端的性能情況 這兩個問題,但瑕不掩瑜,可以作為 SYN 的首選方案。同時,這兩個問題我們將通過添加另外一種技術(shù)方案:真實用戶監(jiān)控(RUM)來解決。
真實用戶監(jiān)控 ( 簡稱 RUM)
顧名思義,指監(jiān)控運行在真實用戶終端(瀏覽器),采集用戶運行頁面時候真實的性能指標(biāo)。業(yè)內(nèi)技術(shù)方案主要分為兩種:
依托 W3C 組織且各瀏覽器廠商廣泛支持技術(shù)方案:PerformanceTiming 和 PerformanceNavigationTiming。偏向從瀏覽器處理過程角度去衡量頁面運行時各節(jié)點、各階段的耗時。
各廠根據(jù)實際需求自研技術(shù)方案。谷歌 web-vitals 是其中最優(yōu)秀的代表,它從用戶體驗角度,用更通俗易懂的指標(biāo)展現(xiàn)頁面性能。
除上述兩種常見技術(shù)方案外,少數(shù)商業(yè)前端監(jiān)控服務(wù)廠商,除了支持 W3C 和 web-vitals 外,還提供少數(shù)自定義性能指標(biāo),如:阿里 ARMS 中的 FMP 、字節(jié) WebPro 里的 SPA_LOAD 。SPA_LOAD 用于評估 SPA 非首頁的頁面性能,有較大創(chuàng)新性,后面還會提及。
技術(shù)選型
技術(shù)選型主要解決兩個問題:1)W3C 的 PerformanceTiming 和 PerformanceNavigationTiming 兩規(guī)范,應(yīng)該以哪個為主?2)W3C Timing 規(guī)范 和 web-vitals 應(yīng)該如何協(xié)作?
W3C 的 PerformanceTiming 和 PerformanceNavigationTiming 兩規(guī)范,應(yīng)該以哪個為主?
PerformanceTiming:已被最新 W3C 標(biāo)準(zhǔn)廢棄,不過當(dāng)前主流瀏覽器仍支持,舊瀏覽器支持好,兼容度高。
PerformanceNavigationTiming:最新標(biāo)準(zhǔn)隨 Navigation Timing Level 2 于 2019 年推出,Navigation Timing Level 2 目的是代替涵蓋 PerformaceTiming 的 Navigation Timing Level 1。
變更點:
- 整合 PerformanceTiming 和 PerformanceNavigation 功能。
- 廢棄因各瀏覽器廠商實現(xiàn)不一,指導(dǎo)意義不足的 domLoading 節(jié)點。
- 添加 ServiceWorker 相關(guān)節(jié)點。
- 各屬性節(jié)點時間使用高精度、以 startTime 為起點的相對時間。
優(yōu)點:
- 使用高精度的相對時間,避免因用戶端系統(tǒng)時間更改而導(dǎo)致后續(xù)節(jié)點值不準(zhǔn)。
- 支持 ServiceWorker 相關(guān)統(tǒng)計。
缺點:
- 瀏覽器兼容性不足。
結(jié)論:
從 Can I Use 統(tǒng)計兩者兼容度僅差 2.67%,但是從我們實際用戶分布來看,使用 PerformanceNavigationTiming,用戶兼容度下降 12%,難以接受。所以我們以 PerformanceNavigationTiming 為主,如瀏覽器不支持,則使用 PerformanceTiming。兩者數(shù)據(jù)格式差異不大,忽略 PerformanceTiming 中 domLoading 節(jié)點,使用 PerformanceTiming 時認(rèn)為瀏覽器不兼容 workStart 節(jié)點即可。
W3C Timing 規(guī)范 和 web-vitals 應(yīng)該如何協(xié)作?
PerformanceNavigationTiming ( 以下簡稱 Timing ): 基于 W3C 規(guī)范,著重從瀏覽器處理過程角度衡量頁面性能,以下簡稱 Timing。
優(yōu)點:
- 瀏覽器兼容性好瀏覽器兼容性好。
- 數(shù)據(jù)豐富,指標(biāo)全面。即包含各階段耗時,如:Unload、Redirect、DNS、TCP、SSL、Response、DomContentLoadedEvent 和 LoadEvent;還支持顯示頁面運行到各節(jié)點時的耗時,如:workStart、fetchStart、requestStart、domInteractive 和 domComplate;也可以根據(jù)所給節(jié)點值,計算 DCL ( DomContentLoaded ) 、window.load 事件或者 PageLoad(頁面加載)的耗時。
缺點:
- 缺乏關(guān)鍵指標(biāo)。雖然指標(biāo)多、全,但是不夠直觀,很難表達用戶體驗效果。
web-vitals: 目前含 6 個指標(biāo):TTFB、FCP、LCP、FID、INP 和 CLS,其中 FID 將被 INP 代替。TTFB、FCP 和 LCP 反映頁面加載性能,F(xiàn)ID 與 INP 代表頁面交互體驗,CLS 表示頁面視覺穩(wěn)定性。僅 6 個指標(biāo),就能支持對頁面加載、交互和視覺穩(wěn)定方面的評估。不過 web-vitals 部分指標(biāo)源頭還是來自于 W3C 制定的 LargestContentfulPaint、LayoutShift、PerformanceEventTiming 和 PerformancePaintTiming 等規(guī)范,不過兼容性更好、整體性更強。
優(yōu)點:
- 指標(biāo)簡明、精練、易懂。
- 自帶基線,能根據(jù)指標(biāo),判斷頁面性能優(yōu)劣。
缺點:
- 瀏覽器兼容不足,尤其在 IOS 端。
- LCP 可以偽造。做法:給頁面添加大尺寸白底圖,該圖加載時間大概率就是 LCP 值,但是該 LCP 值并沒有任何業(yè)務(wù)意義
- 受限于 LCP、CLS 原理,對采集指標(biāo)時機有一定要求,后面會詳細介紹。
述求
真實、客觀、全面衡量頁面性能。
結(jié)論
同時采集 Timing 和 web-vitals 數(shù)據(jù),帶來好處有:
- 指標(biāo)豐富、數(shù)據(jù)全面,既能利用 Timing 站在瀏覽器角度反映各節(jié)點、各階段處理耗時,也能通過 web-vitals 直觀表達用戶視覺體驗。建議先通過 web-vitals 直觀判斷頁面性能,再通過 Timing 再進一步分析,達到綜合考慮、全面分析,減少因瀏覽器兼容不足、LCP 造假等情況下,誤判頁面性能。
- 結(jié)合 Timing 和 web-vitals 數(shù)據(jù),更容易定位問題。如:web-vitals 采集到的 TTFB 慢,可以通過 Timing 定位到具體慢在 Unload、Redirect、DNS、TCP、SSL 哪個階段。
- 解決 web-vitals 老瀏覽器兼容不足的問題。如果瀏覽器不支持 web-vitals, 可以通過 DCL、window.load 事件或者 PageLoad(頁面加載)的耗時來判斷頁面性能。
小節(jié):RUM 技術(shù)選型,同時采集 PerformanceNavigationTiming 和 web- vitals。如果瀏覽器不兼容 PerformanceNavigationTiming,則以 PerformanceTiming 代替。
采集哪些指標(biāo)
我們的需求是:在盡可能不影響頁面性能,既能客觀、全面衡量頁面性能,又能幫助研發(fā)同學(xué)快速定位性能瓶頸點。具體包含三方面需求:1) 指標(biāo)要全面客觀;2) 能發(fā)現(xiàn)慢頁面的瓶頸點;3) 滿足前面兩需求前提下,盡可能不影響頁面性能。
指標(biāo)要全面客觀
首先,我們采集了 web- vitals 六個指標(biāo),效果如下:
谷歌計劃于 2024 年用 INP 替換 FID,F(xiàn)ID 體現(xiàn)第一次交互的延遲時間,INP 表示所有交互中最長的延遲時間。我們認(rèn)為 FID 和 INP 都有各自使用場景,同時保留并不矛盾。
其次,我們對 PerformanceNavigationTiming 做了加工處理,效果如下:
與下面的 W3C 示例圖不同:
原因在于:
- 實際頁面運行過程中,各階段并不一定如上圖那樣串行運行。存在 responseEnd 耗時大于 domLoading 的情況。
- HTTP Cache 階段并無起止時間節(jié)點,只能表明發(fā)生在 fetchStart 和 domainLookUpStart 節(jié)點之間。
- ServiceWorkerInit、ServiceWorkerFetchEvent 和 Request 階段只有起始節(jié)點,沒有終止節(jié)點,無法統(tǒng)計階段耗時。對于 Request 階段不能以 responseStart 作為終止節(jié)點,因為內(nèi)容在網(wǎng)絡(luò)中是分幀傳送的,不一定可以一次傳輸整個頁面內(nèi)容。
- Processing 階段以 domInteractive 為起點,不符合客觀規(guī)律,頁面執(zhí)行到 domInteractive 時候 DOM is ready,所以 Processing 階段無法代表頁面處理過程。
所以我們結(jié)合 W3C 示例圖,以點、段和線的方式,展示真實的頁面運行過程:
- 點:指不存在終止節(jié)點的節(jié)點,含:workStart、fetchStart、requestStart、domInteractive 和 domComplete,用白色圓點表示。
- 段:指真實存在起始和終止節(jié)點的處理階段,如 unload 階段值為:unloadEnd - unloadStart。同理于 redirect、DNS、TCP、SSL、Response、domContentLoadedEvent 和 loadEvent,用藍色柱狀圖表示。
- 線:含頁面加載過程中觸發(fā)的事件,如 DCL ( DomContentLoaded ) 、window.load。另外我們自定義 PageLoad 事件,表示整個頁面加載耗時,值為:loadEventEnd-loadEventStart。用黃色柱狀圖表示。
段和線具體算法:
- UnloadEvent = unloadEventEnd - unloadEventStart
- Redirect = redirectEnd - redirectStart
- DNS = domainLookupEnd - domainLookupStart
- TCP = connectEnd - connectStart
- SSL = connectEnd - secureConnectionStart
- Response = responseEnd - responseStart
- loadEvent = loadEventEnd - loadEventStart
- DCL = domContentLoadedEventStart - startTime
- WindowLoad = loadEventStart - startTime
- PageLoad = loadEventEnd - startTime
此外,為了更全面體現(xiàn)頁面性能,還采集和統(tǒng)計了如下輸入:
- 小概率 (1%) 的采集完整 PerformanceEntry 數(shù)據(jù)。PerformanceEntry 包含 LargestContentfulPaint、LayoutShift、PerformanceEventTiming、PerformanceLongTaskTiming、PerformanceNavigationTiming、PerformancePaintTiming、PerformanceResourceTiming、PerformanceServerTiming 等方面數(shù)據(jù),既包含頁面本身性能指標(biāo),還涵蓋資源、網(wǎng)絡(luò)、緩存、JS 長阻塞任務(wù)、慢執(zhí)行事件等多方面信息,對評估頁面性能,判斷慢頁面瓶頸點很有幫助??紤]到大部分頁面內(nèi)容較多,導(dǎo)致 PerformanceEntry 集合條數(shù)太大,要是 100%采集上報,對帶寬、存儲、查詢性能都有較大影響,所以只能小概率采集。
- 頁面導(dǎo)航類型,取值于 PerformanceNavigationTiming.type,判斷頁面是首次加載還是刷新重載等。
- 按資源類型,統(tǒng)計各類資源個數(shù)、總傳輸體積和總耗時。
- 按域名,統(tǒng)計各域名資源個數(shù)、總傳輸體積和總耗時。
能發(fā)現(xiàn)慢頁面瓶頸點
我們參考Lighthouse 50 分線 和 HTTP Archive 站點統(tǒng)計數(shù)據(jù),根據(jù)應(yīng)用類型是 PC 或 M 來制定慢頁面標(biāo)準(zhǔn),具體閾值如下:
針對慢頁面,我們除了采集上節(jié)提到的 PerformanceNavigationTiming、web- vitals、小概率的完整 PerformanceEntry 數(shù)據(jù)和統(tǒng)計數(shù)據(jù)外,我們還會采集:
- TOP N 慢資源,做法:取 duration 值最大的 N 個 PerformanceResourceTiming 類型的 PerformanceEntry。
- 長任務(wù),指占用 UI 線程大于 50 毫秒的任務(wù)。做法:采集所有 PerformanceLongTaskTiming 類型的 PerformanceEntry。不過當(dāng)前大部分瀏覽器無法提供長任務(wù)所在的腳本地址( containerSrc )和方法名稱( containerName ),采集這部分?jǐn)?shù)據(jù),只能判斷長任務(wù)是否發(fā)生?發(fā)生的次數(shù)。PerformanceLongTaskTiming 內(nèi)容如下:
- 慢事件,指處理時間超過 104ms 的交互事件,做法:采集所有 PerformanceEventTiming 類型的 PerformanceEntry。
- 頁面跳轉(zhuǎn)次數(shù),值為:PerformanceNavigationTiming.redirectCount,可以輔助分析 Redirect 階段耗時多的原因。
- CLS 和 LCP 值大于慢頁面閾值時,記錄其關(guān)聯(lián)的元素。
不影響頁面性能
RUM 必須通過入侵頁面,在頁面引入 JS SDK 來實現(xiàn),不可避免地影響頁面性能。作為一個發(fā)現(xiàn)和分析頁面性能的工具,不應(yīng)該加重頁面性能問題。為了將性能影響降到最低,我們做了兩方面工作:
異步加載 JS SDK:頁面只引入功能單一、體積小的 JS 頭文件,待頁面到達 DomContentLoaded 事件后,以動態(tài) script 方式異步加載功能完整的 JS 主文件。
減少帶寬占用:
- 抽樣上報:慢頁面必須上報,非慢頁面抽樣上報,默認(rèn)抽煙比例為 30%,減少上報次數(shù)。
- 減少上報數(shù)據(jù)體積:全量的 PerformanceEntry 數(shù)據(jù)可以完整體現(xiàn)頁面性能,不少頁面動輒超過白條 PerformanceEntry 數(shù)據(jù),體積過大,所以只能小概率采集全量 PerformanceEntry 數(shù)據(jù),計算和采集 PerformanceEntry 的統(tǒng)計數(shù)據(jù)。
綜上所述我們采集指標(biāo)主要兩大類:PerformanceEntry 和 web-vitals。
SPA 應(yīng)用非首頁性能該如何評估?
采集上述指標(biāo),已經(jīng)可以較為客觀全面評估常規(guī)頁面和 SPA 應(yīng)用首頁的頁面性能。但是 SPA 應(yīng)用非首頁不是瀏覽器標(biāo)準(zhǔn),在 SPA 路由切換過程中:
- 瀏覽器僅會執(zhí)行 History.replaceState()方法,不會也不該重新生成 PerformanceNavigationTiming 數(shù)據(jù)。
- 多數(shù) PerformanceEntry 數(shù)據(jù)包含 SPA 首頁以來所有路由切換頁面的性能指標(biāo)。以 PerformanceResourceTiming 為例,無法通過 History.replaceState()方法拿到真實路由切換的時機,排除 PerformanceResourceTiming 集合中的歷史數(shù)據(jù),從而獲取當(dāng)前路由的 PerformanceResourceTiming 數(shù)據(jù)。因為各前端框架在 SPA 路由切換過程中,大多數(shù)會先執(zhí)行其框架內(nèi)部邏輯,而后再觸發(fā) History.replaceState()方法,所以 History.replaceState()方法觸發(fā)時間晚于路由切換的執(zhí)行時間。
- web-vitals 暫時也不支持采集 SPA 非首頁路由切換后的性能指標(biāo)。
所以,對于上述指標(biāo) SPA 非首頁無法或無法準(zhǔn)確拿到,所以我們暫不評估 SPA 非首頁性能。
針對業(yè)內(nèi) SPA 非首頁性能難評估的情況,字節(jié) WebPro 創(chuàng)造性地推出 SPA_LOAD 的概念,基本邏輯為:以觸發(fā) history.replaceState() 方法為起點,通過 MutationObserver 監(jiān)聽 dom 變更、資源加載、請求發(fā)送等變更事件來尋找一個頁面達到穩(wěn)定態(tài)的時間為終點,通過計算起終點之間的耗時,來衡量 SPA 非首頁的頁面性能。SPA_LOAD 類似于常規(guī)頁面 onload 事件,但是起始時間比真實路由切換時間晚,到終止時間時頁面可能已經(jīng)加載完畢,略有不足,不過已是當(dāng)前最佳的方案,后面我們可能引入。
采集指標(biāo)時機
只有采集真實、準(zhǔn)確的指標(biāo)值,才能真實反映頁面性能,反之,可能誤導(dǎo)產(chǎn)研同學(xué),錯誤評估真實的頁面性能。所以選擇采集指標(biāo)時機有幾大原則:
- 指標(biāo)準(zhǔn),最關(guān)鍵是要保證指標(biāo)是準(zhǔn)的,不準(zhǔn)不如不采。
- 樣本多,上報可靠,部分指標(biāo),如 CLS、INP、PerformanceEventTiming 等,越晚采集值越準(zhǔn),但是越晚采集,留給上報的時間越少,數(shù)據(jù)上報失敗的概率越大,為了盡量多采集數(shù)據(jù)樣本,我們不能等到頁面關(guān)閉時再采集指標(biāo)、提交上報。
- 公平,部分指標(biāo),如 CLS、INP、LCP 等,隨著頁面打開時間邊長,值可能也跟著變大。對于這類指標(biāo),我們只能保證數(shù)據(jù)”樣本多“的前提下,選擇一個相對合理、對“所有項目”都公平的采集時機。
- 一次上報,web-vitals 中部分指標(biāo)如:LCP、CLS、INP,每次變更都會觸發(fā)其回調(diào)函數(shù),谷歌官方建議每次指標(biāo)值變更都采集上報。這種處理邏輯,指標(biāo)值是更準(zhǔn),但是占用太多前端的連接、帶寬和 CPU 資源,也嚴(yán)重加大后端接收程序的處理難度,不是各合理均衡的選擇。所以我們要找到一個合適的采集時機,一次采集并上報所有的性能指標(biāo)。
那么如何確定采集時機?我們得先分析 PerformanceEntry 和 web-vitals 兩類指標(biāo)數(shù)據(jù)得準(zhǔn)確生成時間:
對于 PerformanceEntry 數(shù)據(jù),onload 事件觸發(fā)時,頁面已接近加載完畢,PerformanceEntry 中影響首屏加載的絕大部分指標(biāo)數(shù)據(jù)已經(jīng)生成。未生成的數(shù)據(jù)對評估頁面性能影響不大,如:PerformanceNavigationTiming 中的 loadEventEnd 指標(biāo)值。所以我們認(rèn)為 onload 事件觸發(fā)件時,可以采集 PerformanceEntry 指標(biāo)。
web-vitals 中各指標(biāo)生成原理不一,onload 事件觸發(fā)時:
- TTFB、FCP 指標(biāo)已生成且不會變,可以采集。
- LCP 對應(yīng)的最大元素大概率已加載完畢,所以我們認(rèn)為這時候 LCP 值大概率是準(zhǔn)的,可以采集。
- CLS 值無法確定是否準(zhǔn)確,其計算邏輯為:頁面打開后每 5s 作為一個 session 窗口,累加該窗口內(nèi)產(chǎn)生的偏移值即是 CLS 值,如果下一個 session 窗口的 CLS 值大于上一個 session 窗口,則替換。所以對于 CLS 指標(biāo)來說,打開頁面 5S 后、最好 5S 或其整數(shù)倍時采集,比較合適,對”所有項目“也比較公平。
- FID、INP 也無法確定是否準(zhǔn)確,它們依賴用戶交互操作后才生成,交互操作含:點擊、輸入、拖放、觸摸等事件。FID 是第一次交互的延遲時間,INP 取多次交互操作中延遲時間最大的值。這兩指標(biāo)都依賴于用戶操作,INP 可能會隨著用戶操作次數(shù)變多而值變大,所以任何時間都沒法保證準(zhǔn)確拿到這兩指標(biāo)值。
基于上述考慮,我們認(rèn)為至少要滿足:觸發(fā) onload 事件或打開頁面 5s 之一條件時,才能保證 PerformanceEntry 或部分 web-vitals 指標(biāo)值準(zhǔn)確,采集才有意義。在追求”樣本多“的原則下,結(jié)合 RUM SDK 是 onload 事件后異步加載的實際情況,我們針對頁面是否被正常關(guān)閉前提下,總共設(shè)置了三種采集時機,其特點如下:
為了避免采集指標(biāo)影響頁面性能,我們異步加載 RUM JS SDK,web-vitals 中各指標(biāo)默認(rèn)僅支持異步回調(diào),異步加載再異步回調(diào),導(dǎo)致采集時機時仍可能無法拿到各指標(biāo)值,所以我們對 web-vitals 源碼做了改造,支持同步獲取各指標(biāo)值。
上報方式
采集到指標(biāo)后,需要選擇恰當(dāng)上報方式,將指標(biāo)可靠的發(fā)送到后端。上報方式包含兩個部分:上報機制和上報時機。
選擇合適的上報機制,首先,要滿足功能需求,瀏覽器兼容度高,對數(shù)據(jù)大小最好沒限制;其次,能感知上報請求異常,便于上報重試,進而提高上報可靠性;最后,客戶端支持設(shè)置超時時間,避免長時間占用忘了連接,加大后端服務(wù)壓力。常見上報機制有四種,分別是:Image、XMLHttpRequest、sendBeacon、Fetch API。其特點如下:
Image | XMLHttpRequest | sendBeacon | Fetch | |
基本原理 | 創(chuàng)建一個 1 像素、隱藏的 img DOM,將上報地址和上報內(nèi)容包含在 img 的 src 中。上報成功則返回 200 狀態(tài)碼 | 利用瀏覽器內(nèi)置對象 XMLHttpRequest 上報數(shù)據(jù) | 使用專門設(shè)計用于發(fā)送分析數(shù)據(jù)的 navigator.sendBeacon()方法,以異步發(fā)送 HTTP POST 請求的方式將分析數(shù)據(jù)提交到后端 | fetch 是一個現(xiàn)代的、基于 Promise 的用于發(fā)送 HTTP 請求的 API |
瀏覽器兼容性 | 高 | 高 | 中,不支持 IE | 中偏低,不支持 IE |
數(shù)據(jù)大小限制 | 小于 8K 各瀏覽器大小限制不一,且受制于 CDN、后端代理和 web 服務(wù)器,默認(rèn)值常為 8K。8K 指用 URI 編碼后的長度 | 用 POST 請求,無限制 | 有,部分瀏覽器小于 64K | 用 POST 請求,無限制 |
感知上報異常 | 部分支持。 請求返回狀態(tài)碼為 404 或 204 時會觸發(fā) img onerror 事件。狀態(tài)碼大于等于 400,不會觸發(fā) onerror 事件,如 400、500、502 和 504 等。 | 支持 | 不支持。 sendBeacon()返回值,只能表示瀏覽器是否發(fā)出請求 | 支持 |
可設(shè)置超時 | 不可以,依賴服務(wù)端的設(shè)置 | 可以 | 不可以 | 可以 |
優(yōu)點 | 使用簡單、兼容度高 | 功能強大,靈活易擴展 無大小限制,兼容度高 | 使用簡單,可靠性高 頁面關(guān)閉時,仍可發(fā)送 | 相比于 XMLHttpRequest,使用更簡介、功能更強大 |
缺點 | 數(shù)據(jù)大小有限制,難感知上報異常 | 代碼編寫略復(fù)雜 跨域處理要留意 | 無法感知上報異常,函數(shù)返回值 true、false 只能代表是否發(fā)送成功 | 同 XMLHttpRequest 瀏覽器兼容度最低 |
適用場景 | 上報數(shù)據(jù)小,可靠性要求不高 | 功能需求多 建議以 POST 方式用 text/plain 或 application/x-www-form-urlencoded 格式上報,CORS 預(yù)驗證 | 需要在頁面關(guān)閉時,上報數(shù)據(jù) | 同 XMLHttpRequest,更適用于瀏覽器分布較新的終端 |
上述結(jié)論:依賴于 chrome114
相比于 Fetch,XMLHttpRequest 功能幾乎一致,兼容性更高;與 Image 對比,XMLHttpRequest 具有兼容度高、數(shù)據(jù)大小沒限制、能感知異常和可設(shè)置超時時間等優(yōu)勢;XMLHttpRequest 是頁面正常(未關(guān)閉)情況下,發(fā)送指標(biāo)數(shù)據(jù)的首選上報機制。至于 sendBeacon,雖然存在諸多不足,但是頁面關(guān)閉時仍可以上報數(shù)據(jù),成功率還較高,適合作為在頁面關(guān)閉時,發(fā)送尚未發(fā)送的指標(biāo)數(shù)據(jù)的上報機制。
既然選擇了 sendBeacon 作為頁面關(guān)閉時發(fā)送指標(biāo)數(shù)據(jù)的上報機制,那該如何判斷頁面被關(guān)閉?傳統(tǒng)方案是監(jiān)聽 unload 或 beforeunload 事件,該方案存在兩個不足:
- 不能滿足功能需求。手機端用戶離開頁面時,更習(xí)慣將瀏覽器隱藏,而不是關(guān)閉瀏覽器。隱藏頁面時,不會觸發(fā) unload 和 beforeunload 事件。
- 性能有損耗。部分瀏覽器,監(jiān)聽到 unload 或 beforeunload 事件后,無法使用 bfcache,導(dǎo)致頁面性能降低。
更合適、更現(xiàn)代的方案是監(jiān)聽 pagehide 或 visibilitychange===hiden 事件,該方案除了避免傳統(tǒng)方案存在的兩不足,兼容度也會更高。對于 SPA 項目我們不采集非首頁性能指標(biāo),觸發(fā)History.replaceState()方法也算離開頁面。
綜上所述,整體上報機制為:1)頁面正常(未關(guān)閉),到采集時機時,SDK 采集性能指標(biāo)數(shù)據(jù),使用 XMLHttpRequest 機制上報;2)通過監(jiān)聽 pagehide 或 visibilitychange===hiden 事件,當(dāng)頁面被關(guān)閉時,如果滿足任一采集時機條件,則立即采集指標(biāo),使用 sendBeacon 機制上報。
整體方案
綜上所述,我們整理 RUM 實施大綱如下:
實際編碼過程中,具體處理流程如下:
優(yōu)缺點
在實施過程中,我們總結(jié) RUM 優(yōu)缺點如下:
RUM 架構(gòu)設(shè)計復(fù)雜,實施成本較高,由于是技術(shù)剛需,只能投入資源,努力做好。
針對 無診斷結(jié)果和優(yōu)化建議 的缺點,可以結(jié)合 SYN,取長補短,利用 SYN 診斷慢頁面性能瓶頸點分布。
對于 無評分,沒法判斷頁面性能優(yōu)劣 的問題,我們分三步走:首先,制定慢頁面標(biāo)準(zhǔn),判斷單次頁面是否快慢,標(biāo)準(zhǔn)值前文已有描述;其次,統(tǒng)計該頁面各重要指標(biāo)的 AVG、TP50、TP90、TP99 值,全面評估頁面所有請求的性能分布;最后,我們會對頁面所在的應(yīng)用進行評分,直接告訴研發(fā)同學(xué),該應(yīng)用性能優(yōu)劣,應(yīng)用評分具體做法,會在下面《建立評分體系》章節(jié)細講。
三、整體架構(gòu)設(shè)計
前文深入分析了 SYN 和 RUM 各自特點、使用方法及優(yōu)缺點等,我們發(fā)現(xiàn) SYN 和 RUM 各有所長、無法替代,最好同時引用 SYN 和 RUM,構(gòu)建體系化性能監(jiān)控架構(gòu),提供性能監(jiān)控和性能分析工具鏈,支持產(chǎn)研同學(xué)在 devpos 各階段中發(fā)現(xiàn)和定位頁面性能問題。
在編碼、構(gòu)建和測試階段,研發(fā)同學(xué)可以利用 SYN 來做頁面性能測試,判斷頁面性能是否達標(biāo)?如果頁面性能有問題,再利用 SYN 診斷性能問題,獲得優(yōu)化建議。此舉解決前端頁面長期以來,前端頁面做性能測試難、交付頁面質(zhì)量無標(biāo)準(zhǔn)等痛點。應(yīng)用部署后,再利用 RUM 采集真實用戶頁面性能,評估真實頁面性能,如果還存在慢頁面,仍可以使用 SYN 定位慢頁面性能瓶頸點,并利用診斷結(jié)果和優(yōu)化建議,提高優(yōu)化效率。除此之外,SYN 還可以用來做競品對比,達到知己知彼、快競爭對手一步的目的。
有了 SYN 和 RUM,我們可以構(gòu)建在線持續(xù)優(yōu)化慢頁面的閉環(huán),如上圖。RUM 負責(zé)采集真實用戶產(chǎn)生的慢頁面,經(jīng)后臺存儲、聚合,自動將 TOPN 的慢頁面創(chuàng)建 SYN JOB,待 JOB 運行完畢,將診斷結(jié)果和優(yōu)化建議以告警防止通知研發(fā)同學(xué),研發(fā)同學(xué)利用 SYN 優(yōu)化建議,再利用 devtools、webpack 等工具,改進頁面,交付高質(zhì)量頁面,降低慢頁面的頻次。如此循環(huán)迭代,持續(xù)優(yōu)化應(yīng)用頁面性能,最終應(yīng)用能達到極致性能。
三、建立評判體系
引入 SYN,自研 RUM SDK,采集到眾多 SYN 和 RUM 指標(biāo)數(shù)據(jù)后,我們將著手建立評判體系,評估各應(yīng)用、團隊、部門,乃至整個公司的性能情況,輸出少數(shù)幾個關(guān)鍵指標(biāo)和評分,直觀告訴各層級、各角色員工其所在組織及同級組織的頁面性能情況,通過得分和同級對比,評判是否要優(yōu)化頁面性能?
建立評判體系時我們秉持著:既要突出重點、關(guān)鍵指標(biāo),同時還能全面、綜合、客觀真實地反映頁面性能 的原則。所以我們將評判體系分為兩大塊,其一,展示最關(guān)鍵的性能指標(biāo)。其二,輸出各指標(biāo)、應(yīng)用、團隊、部門及公司層級的性能評分和層級。
展示最關(guān)鍵的性能指標(biāo)
我們從 web-vitals 和 performanceNavigationTiming 中各選一個最能代表頁面性能的指標(biāo),分別為:DCL 和 LCP。LCP 兼容度不高且可能被偽造,DCL 既能代替 LCP 部分反映首屏性能且兼容度高、難以偽造,和 LCP 相輔相成,最能體現(xiàn)頁面性能。
TP90 代表 90%用戶的體驗下限,與 AVG、TP50、TP75 比,覆蓋和統(tǒng)計更廣的用戶,還能屏蔽頁面在手機端特殊網(wǎng)絡(luò)環(huán)境下,產(chǎn)生的少數(shù)臟數(shù)據(jù),更具有代表性。
輸出各級評分
應(yīng)用性能評分
為了全面、綜合和客觀的評估應(yīng)用頁面性能。我們將選擇各維度具有代表性指標(biāo),參考 HTTP Archive 給予的業(yè)內(nèi)指標(biāo)分布,根據(jù)應(yīng)用特性,如應(yīng)用類型( PC 或 M 端 )、終端用戶( C 端用戶、B 端客戶和內(nèi)部員工),設(shè)立不同的評分基線,算出各指標(biāo)得分。再根據(jù)各指標(biāo)重要程度設(shè)置權(quán)重,通過加權(quán)平均算法,求得應(yīng)用性能評分。流程如下:
獲取應(yīng)用性能分過程中涉及幾個重要流程:1) 指標(biāo)篩選及權(quán)重設(shè)置;2) 選擇評分算法;3) 確立指標(biāo)基線;4) 使用加權(quán)平均算法計算應(yīng)用性能分。下面逐一介紹。
指標(biāo)篩選及權(quán)重設(shè)立
此過程中,我主要考慮兩點:
全面綜合考慮。頁面性能涵蓋多方面,傳統(tǒng)上只用首字節(jié)、白屏、首屏?xí)r間等少數(shù)指標(biāo)來衡量,較為片面,不夠客觀。我們認(rèn)為評判頁面性能應(yīng)該涵蓋各維度指標(biāo),如:頁面加載、交互體驗和視覺體驗。此外我們還引入慢 API 比例的概念,API 請求比例指頁面打開后 API 耗時超過 3s 的請求比例,慢 API,即可能影響首屏加載耗時,也會影響交互過程中的用戶體驗。為了讓研發(fā)同學(xué)關(guān)注慢頁面、將在線 SYN 作為日常開發(fā)性能評估工具,我們將 SYN 評分也作為權(quán)重指標(biāo),后臺系統(tǒng)每天會統(tǒng)計訪問次數(shù) TOP10 的慢頁面并自動創(chuàng)建 SYN 定時任務(wù),待任務(wù)執(zhí)行、分析完畢后,將優(yōu)化建議和診斷結(jié)果通知研發(fā)同學(xué)。SYN 評分項,包含性能分和最佳實踐分,兩者都是百分制。
突出重點。提高重要指標(biāo)的權(quán)重比。如:加載指標(biāo)用于衡量頁面能不能用,最為關(guān)鍵,所以賦予權(quán)重占比最大。LCP 是最重要的加載指標(biāo),權(quán)重占比也相應(yīng)提高。由于 LCP 本身不一定完全合理且可能被偽造,所以評判頁面加載性能時,還引入 DCL、FCP、TTFB、WindwLoad 和 PageLoad 等加載指標(biāo)。該做法,優(yōu)點:指標(biāo)多,維度廣、角度大、更全面和更客觀準(zhǔn)確;缺點:增加評判系統(tǒng)復(fù)雜度和難度。
基于上述兩點考慮,我們指標(biāo)篩選結(jié)果和權(quán)重占比設(shè)置,如下圖:
各指標(biāo)以其 TP90 統(tǒng)計值,參與評分運算。
選擇評分算法
評分算法主要參考 Lighthouse 評分曲線模型,基本原理是:設(shè)置兩個控制點,通常是 TP50 和 TP90,即得 50 或 90 分時的指標(biāo)值點,然后根據(jù)這兩個控制點,生成對數(shù)正態(tài)曲線,通過該曲線可以獲得任一指標(biāo)值對應(yīng)的得分。下圖是 M 端 LCP 的評分曲線:
m 表示中位數(shù),圖中 m 值為 4000ms,表示當(dāng) LCP 值為 4000ms 時,得 50 分;同理 p10 為 2520ms,LCP 值為 2529ms 時,得 90 分。設(shè)置 m 和 p10 后,會生成右邊的評分曲線模型,根據(jù)該模型可以獲得 LCP 值從 0 到正無窮時的得分。
確立指標(biāo)基線
確立指標(biāo)基線是為了給評分算法提供兩個控制點,即 m 和 p10 的值。確立方法有三種:
- 直接借用 Lighthouse 配置。以 Lighthouse 對應(yīng)指標(biāo)的 50 分和 90 分閾值為 m 和 p10 控制點值,如 web-vitals 中各指標(biāo)。
- 參考 HTTP Archive 統(tǒng)計數(shù)據(jù),以統(tǒng)計數(shù)據(jù)中的 p10、p75 值為 m 和 p10 控制點值。如 performanceNavigationTiming 各指標(biāo)。
- 自建基線。少數(shù)指標(biāo)基線無法從上述兩種方法確立,只能自建基線。具體做法:以當(dāng)前后臺系統(tǒng)拿到的數(shù)據(jù)為樣本,以樣本的 tp75 和 tp95 值為 m 和 p10 控制點值。適用于慢 api 比例指標(biāo)。
確立基線過程中,應(yīng)考慮應(yīng)用價值和研發(fā)要求的不同,根據(jù)應(yīng)用特性,有針對性的設(shè)置。首先,PC 端和 M 端類型的應(yīng)用,要設(shè)置不同的指標(biāo)基線,所幸 Lighthouse 和 HTTP Archive 都提供 PC 端和 M 端的配置參考;其次,根據(jù)應(yīng)用實際終端用戶類型,有針對性的調(diào)整閾值。C 端和 B 端應(yīng)用,直接產(chǎn)生業(yè)務(wù)價值,性能要求要比內(nèi)部應(yīng)用高,兩控制點值應(yīng)該更低。
使用加權(quán)平均算法計算應(yīng)用性能分
根據(jù)指標(biāo) tp90 值、指標(biāo)基線和評分算法,求得該指標(biāo)的百分制得分。
使用加權(quán)平均算法求應(yīng)用性能分,結(jié)果 = (Σ (指標(biāo)tp90值 × 指標(biāo)權(quán)重)) / (Σ 權(quán)重) ,其中:Σ 表示求和。
各級組織評分
至于求各級組織,含團隊、部門及公司的性能分,則根據(jù)其管轄的應(yīng)用個數(shù)、應(yīng)用性能分及應(yīng)用權(quán)重,仍使用加權(quán)平均算法獲取組織分。流程如下:
求組織性能分的難點是:如何設(shè)置應(yīng)用權(quán)重?我們主要參考應(yīng)用的 PV 區(qū)間和應(yīng)用級別。PV 層級越大、應(yīng)用級別越高,權(quán)重越大。具體配置參考如下:
PV區(qū)間中的PV 值指采用 PV,而非真實 PV,采用 PV=采集到 RUM 數(shù)據(jù)量/抽樣比例。
經(jīng)過上述步驟,我們能獲得應(yīng)用性能分,以及各級組織的性能分,如團隊性能分、部門性能分及之家性能分,展示效果如下:
性能分參考lighthouse標(biāo)準(zhǔn),50分以上算合格,90分以上才算優(yōu)秀。
四、總結(jié)
通過頁面性能監(jiān)控和評判體系建設(shè),我們既有原始頁面性能數(shù)據(jù),又有聚合統(tǒng)計值,還有最終評分。通過評分、統(tǒng)計和原始數(shù)據(jù),打通了發(fā)現(xiàn)、定位和分析性能問題的鏈路。研發(fā)同學(xué)可以通過評分直觀判斷應(yīng)用性能優(yōu)劣是否需要優(yōu)化?如果需要優(yōu)化,再通過聚合統(tǒng)計數(shù)據(jù),分析應(yīng)用瓶頸點;定位具體瓶頸點時,可以查看明細數(shù)據(jù),輔助分析產(chǎn)生瓶頸點的具體原因;改進后可以通過通過統(tǒng)計查看優(yōu)化效果,最終反映到提高評分上。
受限于篇幅,本文僅能介紹頁面性能監(jiān)控和評判體系建設(shè)相關(guān)的實踐。一個完整的頁面性能監(jiān)控系統(tǒng),還應(yīng)該包含:監(jiān)控、報警、優(yōu)化、治理等模塊,不僅僅只是指標(biāo),能度量頁面性能,發(fā)現(xiàn)其中性能瓶頸點,幫助研發(fā)同學(xué)提升優(yōu)化效率,還要治本,通過構(gòu)建一系列前端工具鏈,改進交付過程,通過規(guī)范、工具和流程,從源頭上提高頁面交付質(zhì)量,避免將問題帶到線上,先于用戶發(fā)現(xiàn)性能問題。
五、參考文獻
Web Vital Metrics for Single Page Applications
一個收集和分析網(wǎng)站性能數(shù)據(jù)的項目,旨在幫助 Web 開發(fā)者了解互聯(lián)網(wǎng)的技術(shù)趨勢以及性能優(yōu)化的最佳實踐。