得物技術前端性能監控實踐

前言


對於前端來講,最重要是的體驗,而在前端體驗中,最爲核心的就是性能。秒開率、流暢程度等一系列指標都直接影響用戶體驗。css

所以,創建一個準確、及時、有效的前端性能監控系統,不只能夠量化當前頁面的性能水平,還能夠爲優化方案的效果提供數據支持,此外,還能夠在頁面性能下滑時提供報警服務,提醒開發人員改善頁面性能。前端

監控指標的選取

在參考前人的實踐成果後,咱們對性能監控的一系列指標的計算成本,適用性和實用價值進行了評估,認爲如下指標和信息是最具實用性和性價比的:python

首先是 fcp(first contentful paint,以下圖所示),這個指標是目前統計頁面秒考慮的主流指標,雖然它不如 fmp(First Meaningful Paint、lcp(Largest Contentful Paint)、speedIndex 等指標更貼近用戶真實使用體驗,可是優勢是在 Android 端經過調用 Performance API 便可得到,在 iOS 能夠經過 raf(requestAnimationFrame)估算,實施過程簡單。
web

其次是 tts(time to server),這個指標並無出如今此前看到的文章裏邊。數據庫

它描述的是用戶鏈接到服務器的時間,經過 Performance API 中提供的 requestStart 減去 fetchStart 獲得,這個指標不是前端能夠經過技術能夠優化的,可是它能夠反映在當前用戶羣體的網絡環境下,頁面秒開率的上限是多少。後端

舉個例子,假如經過性能監控數據發現,有 15%的用戶訪問,須要花費至少 1s 的時間才能鏈接到咱們的服務器(能夠是 SSR 服務器,也能夠是 CDN),那麼這些用戶不管如何都不能秒開,那麼此時,某頁面秒開率的上限就是 85%。瀏覽器

假如當前狀況下,這個頁面的秒開率已經達到了 75%甚至更高,那麼繼續優化的邊際收益會很是低,應該適可而止了。
服務器

第三是 tsp(time for server processing),這個指標也沒有出如今此前的參考文章裏。微信

它針對的是在使用 SSR 的場景下,服務器內部處理頁面請求的耗時,可經過 Performance API 中提供的 responseStart 減去 requestStart 獲得。網絡

這個環節性能太差,亦會成爲拖累秒開率的一個瓶頸,因此必須予以監控;tsp 太長會壓榨其餘環節的性能預算,過小會增大服務器運維成本。

第四是 css 文件、圖片等資源的大小、xhr 請求的持續時間,前兩種資源若是不加節制,會致使頁面即使作到秒開,也沒法快速進入可用狀態,好比常見的 feed 流頁面。

對於 xhr,須要進行分類討論,若是是 SSR 頁面,則影響不大,只要保障 tsp 處於較低水平,基本上不會拖累秒開率,但若是是 SPA,頁面主要功能區都依賴後端數據支持的,好比判斷權限、展現 feed 流內容,xhr 的響應速度就很是關鍵了,也須要予以監控,在指標下滑時,通知後端予以優化和處理。

最後是一些環境信息,好比用戶實用什麼品牌和型號的手機,是在微信、瀏覽器、仍是咱們的哪一個版本的得物 App 內打開 H5 頁面。

當頁面的性能出現問題時,這些輔助信息能幫助咱們儘量精準地復現用戶的實用場景,高效、準確地解決問題。

系統架構

整個系統由如下模塊構成:

SDK:負責採集用戶的頁面性能數據和基本信息,按照必定的發送策略將性能數據發往 SLS,植入頁面後能夠自行採集性能數據,不須要和頁面代碼交互。

SLS:阿里雲日誌服務,接受 SDK 發送的數據數據,併爲性能數據添加接收時間,ip 等附加信息。

Backend:性能數據後端服務,這個模塊有兩個功能,一個是定時從 SLS 拉取原始的性能數據,對其進行去重和加工,獲得性能指標數據和用戶信息數據,而後將這些數據分門別類地存入對應的數據表中,以備查詢。另外一個是爲數據可視化提供接口數據。

DB:持久化處理後的性能日誌數據和性能指標數據的數據庫。

Report: 性能數據報表,經過和操做報表,觀察特定項目、版本下的指定頁面的各項性能指標。

各模塊關係以下圖所示:

關鍵技術決策

在進行開發以前,須要對系統的幾個關鍵點進行思考和決策。大體有如下幾個點:

  1. 在移動端,unload 事件並非總能觸發,因此須要 SDK 可以間歇性地發送數據到 SLS。爲了控制發送的頻率,同時減小數據的重複量,咱們採起了一種發送間隔逐步延長的策略,即當前頁面打開的時間愈來愈長,數據發送的頻率會愈來愈低,該頁面打開的時間達到必定長度後,SDK 將完全中止工做。
  2. 因爲數據存在重複,就須要對用戶的端(瀏覽器、微信、app 內的 webview)進行指紋計算,這裏咱們選用了 fingerPrintJS2,在計算時,咱們去掉了致使指紋不穩定的瀏覽器特徵,使用戶在打開頁面的時候,老是有固定的指紋。可是隻依賴指紋是不夠的,由於同一個型號的手機,算出來的指紋極可能是同樣的。因此,在對性能數據去重的時候,就要同時結合用戶的指紋、日誌的客戶端時間戳、用戶設備的 ip 來進行去重,使用同一個 wifi,同一種設備,在同一毫秒打開頁面的用戶,以目前前端頁面的訪問量和時間分佈來判斷。這個去重方案的效果仍是很是好的。
  3. SDK 有一部分同步代碼,而且須要在頁面載入後儘量及早運行。這意味着若是 SDK 報錯,會致使頁面沒法正常運行,這是很是危險的。因此用 try catch 包裹 SDK 的同步代碼,確保 SDK 的異常不會拖累頁面。
  4. 某些頁面加載的圖片、發送的請求會很是多,所有記錄加以上報是很是不現實的,因此咱們在監控這部份內容時,只把文件字節數排名前 10 的圖片,加載時長排名前 10 的請求的詳情記錄下來,而後統計一下這個頁面加載過多少圖片,發送過多少請求。這樣既知道頁面資源加載的規模,又能找到頁面加載最耗時的資源。
  5. 原始日誌選擇發送到 SLS,主要是由於這個數據發送的併發量很是大,本身作日誌服務器成本太高,用 SLS 是一個比較划算的選擇。
  6. 後端服務選擇用 Python+Django,Python 屬於腳本語言,雖然性能差了些,可是對於前端同窗來講,上手難度會低一些,對於小規模的後端服務,開發效率也能有保障。同時採用 pypy 編譯器,能夠改善 python 代碼的運行速度。此外,經過使用多進程+多線程,能夠進一步提升數據的處理速度。
  7. 因爲咱們的目的進行統計分析,因此並無必要統計全部的性能數據,爲此,咱們採起了等步長採樣的方法,即針對一天的數據,咱們每 5%的日誌數據,只取前 1000 條進行統計,因爲從用戶側上報數據這一行爲是隨機的,因此這個方案基本上能夠保障採樣的隨機性。這樣一來,統計工做的計算量就大幅降低了,保證咱們能夠用一個性能很弱的機器就能夠完成數據處理工做,也能夠保證一旦出現故障,咱們有時間「追回」以前沒有處理的數據。
  8. 數據庫方面咱們選擇了 MySQL,咱們對 IO 並無很是苛刻的需求,因此中規中矩的關係型數據庫便可知足需求。

將來發展規劃

目前的前端性能監控系統能知足平常的監控需求,可是還能夠更進一步:

  1. 對幀率的統計:SDK 目前其實有統計幀率的功能,可是因爲原始幀率的數據量過大,因此後期須要轉換幀率的統計方式,例如只上報卡頓的時間區間和該區間的幀率分佈。
  2. 使用 lcp、fmp 等更科學的指標來替代 fcp。
  3. 額外結合 pageName 去進行去重,進一步提升去重的效果。

文|老狼

關注得物技術,攜手走向技術的雲端

相關文章
相關標籤/搜索