一、背景
愛番番大前端整體面臨以下問題:
-
Metrics:URL的RED指標不全。URL不全,ERROR缺失,Duration分位置缺失。整體實效性為T+1。無法及時感知問題。只對基本頁面級別的讀操作進行了監控。
-
Tracing:Trace無法全端串聯,直接影響具體case的跟進。無前端Trace。
-
Logging:無Log。Sentry的Error資訊目前僅PC接入。且未進行實際使用;和trace無法打通。
-
報警:對於異常資料缺乏有效報警。
二、目標
-
Metrics目標:RED指標,自定義Metrics
-
Tracing目標:全端單動作追蹤
-
Logging目標:Error級別Log具備。單Traceid和Log能夠實現串聯
2.1 核心目標
從全域性問題出發,能夠洞察統計性的頁面url真實RED指標,以及可以做操作流任意階段之間的統計性耗時分析。從個案問題出發,能夠基於使用者id進行任意一次全端呼叫鏈追蹤。
具體地:
-
頁面級別效能監控。可包含條件檢索和正常頁面重新整理。包含RED
-
能夠基於使用者id進行任意一次全端呼叫鏈追蹤。
-
可分析一個操作流任意階段之間的統計性耗時。
-
準實時呈現。資料延時小於5min。
-
可區分來源、地域、裝置等核心資訊
-
寫請求效能監控。包含新建、編輯、刪除。包含RED
2.2 目標抽象
本質上,抽象為AggrEvent、Event、Trace、Span四個概念。Transaction用來做純埋點,此處暫不需要。
-
帶TraceID(可被覆蓋)和TimeStamp資訊的Event
-
全端Trace下的呼叫鏈。本質上是前端的Event鏈路 + 服務端的Span鏈路串聯而成。
-
考慮到批次傳輸效能。採用AggrEvent進行聚合傳送。
三、 名詞解釋
- RED:RED方法是Weave Cloud在基於Google的“4個黃金指標”的原則下結合Prometheus以及Kubernetes容器實踐,細化和總結的方法論,特別適合於雲原生應用以及微服務架構應用的監控和度量。主要關注以下三種關鍵指標:
- (請求)速率:服務每秒接收的請求數。
- (請求)錯誤:每秒失敗的請求數。
- (請求)耗時:每個請求的耗時。
- optid(operate-id):一個操作唯一對應的一個id。比如一次重新整理,一次修改。
- reqid(request-id):單次請求唯一對應的一個id。如果重試,則reqid設定為新id。一個optid可能會存在多個reqid。
- tid(tracing-id):貫穿服務端端呼叫鏈的唯一id。
- optType: 某一類操作定義。比如一次重新整理,一次修改。
四、目標拆解
4.1 Why
4.1.1 為什麼要做大前端監控體系?
獲取使用者行為以及跟蹤產品在使用者端的使用情況,並以監控資料為基礎,指明產研最佳化的方向。
4.1.2 收益是什麼?
-
Metrics:對各端各頁面各場景RED指標準實時分析、視覺化呈現,打破前端監控黑盒
-
Tracing:打通前後端呼叫鏈,能夠針對任意case進行前後端鏈路分析,讓前端效能最佳化有的放矢
-
Logging:對各端執行時錯誤進行實時監控報警,能透過日誌最大程度還原使用者現場並定位問題,提升前端頁面穩定性
4.2 What
4.2.1 監控什麼端?
愛番番現有產品各端:web,h5(瀏覽器、webview),Android,iOS,客戶端,小程式
4.2.2 監控什麼方向
(1) 資料監控
資料監控,顧名思義就是監聽使用者的行為。常見的資料監控包括:
-
PV/UV:PV(page view),即頁面瀏覽量或點選量。UV:指訪問某個站點或點選某條新聞的不同IP地址的人數
-
使用者在每一個頁面的停留時間
-
使用者透過什麼入口來訪問該網頁
-
使用者在相應的頁面中觸發的行為
統計這些資料是有意義的,比如我們知道了使用者來源的渠道,可以促進產品的推廣,知道使用者在每一個頁面停留的時間,可以針對停留較長的頁面,增加廣告推送等等。
該方向主要為業務服務,目前職責定位更符合神策埋點系統。
(2) 效能監控
效能監控指的是監聽前端的效能,主要包括監聽網頁或者說產品在使用者端的體驗。常見的效能監控資料包括:
-
不同使用者,不同機型和不同系統下的首屏載入時間
-
白屏時間
-
http等請求的響應時間
-
靜態資源整體下載時間
-
頁面渲染時間
-
頁面互動動畫完成時間
這些效能監控的結果,可以展示前端效能的好壞,根據效能監測的結果可以進一步的去最佳化前端效能,比如相容低版本瀏覽器的動畫效果,加快首屏載入等等。
(3) 異常監控
產品的前端程式碼在執行過程中也會發生異常,因此需要引入異常監控。及時的上報異常情況,可以避免線上故障的發上。雖然大部分異常可以透過try catch的方式捕獲,但是比如記憶體洩漏以及其他偶現的異常難以捕獲;
愛番番目前採用sentry進行錯誤日誌採集。
4.2.3 監控什麼內容
1.web
類別
|
內容
|
頁面分析
|
具體豐富的頁面指標,提供如伺服器端響應時間、網路延時、DOM解析和頁面渲染時間等效能指標
幫助研發更快捷的定位服務端、客戶端的頁面問題
|
Ajax請求
|
獲取使用者訪問過程,頁面發出的所有Ajax請求URL、引用頁面URL,監控某一Ajax請求的響應時間、回撥時間、上傳資料量、下載資料量以及響應過程中伺服器返回的錯誤
|
JS error
|
程式碼級定位出錯頁面或者指令碼URL,引用頁面URL,出錯的行列資訊、堆疊等資訊,透過sourceMap定位原始碼檔案,透過pageId,operateId以及tid最大限度還原上下文
監測Web App中JS錯誤的數量,各瀏覽器出錯百分比和JS錯誤率隨時間變化的趨勢
|
瀏覽器監測
|
統計不同版本瀏覽器和瀏覽器型別的平均頁面載入時間和吞吐率
提供基於多平臺瀏覽器效能分析,相容性分析
|
慢頁面追蹤
|
抓取載入時間超過設定閾值的頁面上的元素資訊,及每個元素的TCP建連、首包及剩餘包等所需時間
詳細定位頁面上的哪些元素的載入拖慢了頁面的響應,為最佳化使用者體驗提供依據
|
2.其他端:
h5(瀏覽器、webview)
統計從端點選開始,到指定頁面渲染全流程時間分佈,包括(容器耗時,框架耗時,網路耗時,渲染耗時等)。
統計NPJS框架自身穩定性,以及各個階段耗時。
統計任意多個操作各個時間斷耗時。
iOS,Android
現在使用百度移動端效能中臺,功能基本滿足愛番番監控、報警功能;
移動端監控指標主要為 卡頓和崩潰,目前有崩潰堆疊資訊日誌;暫無互動時長分析(xray平臺有,手百效能中臺沒有)
http://performance.baidu.com/
PC客戶端
electron監控方案參考WEB版
四、方案調研
自建or接入其他平臺?
業務埋點採買神策。
效能監控平臺:
廠內:
效能中臺 http://performance.baidu.com/
日誌中臺 http://app.baidu-int.com/
目前日誌中臺可以接入日誌,但是需求是前端頁面的效能指標,屬於效能平臺範圍,目前效能平臺無前端效能指標建設。
效能中臺的定位主要是針對native,當前通用能力主要是崩潰、卡頓、端異常、Flutter異常、日誌回撈等,其他的一些能力主要在手百、或者相關SDK上,還未對外輸出。經溝通暫不考慮支援前端效能監控。
廠外:效能監控主流收費平臺:
-
ONEAPM https://www.oneapm.com/solutions/qualityacceptance.html
-
聽雲 https://www.tingyun.com/
-
效能魔方 http://www.mmtrix.com/imonitor
-
監控寶 https://www.jiankongbao.com/
收費平臺功能大同小異,均能滿足前端基礎效能監控需求。
優點:有較為成熟的解決方案,能快速滿足多端效能監控基本需求;
缺點:
-
收費
-
無法與部門現有後端APM體系打通
-
無法滿足特定case分析需求
結論:複用神策埋點SDK儲存,上報能力及通路,進行SDK二次封裝;自建日誌服務及展示系統。
五、方案設計 (web)
1、採集
(1)埋點SDK
基於神策SDK進行二次封裝
統一封裝埋點SDK,透過npm包形式進行版本管理;使用方在公共模組對埋點SDK進行初始化。
增加無侵入效能採集能力,提供取樣率配置等可配置擴充套件能力。
參考介面文件
(2)通用統計指標(參考聽雲)
指標
|
統計方式
|
白屏 |
使用者瀏覽器輸入網址後至瀏覽器出現文字或1px圖片所花費時間。計算規則:優先使用Chrome、IE提供的firstPaintTime,沒有獲取到計算head中link、script指令碼下載的最長時間。
|
首屏 |
使用者瀏覽器首屏內所有的元素呈現所花費時間。計算規則:尋找首屏區域內的所有圖片,計算最長載入時間得到首屏時間。
|
可互動 |
功能可以使用的時間,也指domready時間。計算公式:可互動=Navigation Timing API domContentLoadedEventStart – fetchStart。
|
完全載入 |
頁面完全載入總時間。指從NavigationStart事件開始到LoadEventEnd事件結束,計算公式:LoadEventEnd-NavigationStart
|
HTML載入 |
指主HTML檔案從DNS解析到載入完且不包含排隊時間和應用伺服器響應時間,即包含DNS,TCP建連,Request和Response,計算公式:responseEnd-domainlookupStart-排隊時間-應用伺服器響應時間
|
頁面渲染 |
指從responseEnd事件開始到loadEventEnd結束,包含DOM解析和資源載入,計算公式:LoadEventEnd-responseEnd
|
DOM解析 |
指從responseEnd事件開始到DomContentLoadedEventEnd事件結束,計算公式:DomContentLoadedEventEnd-responseEnd
|
資源載入 |
指從DomContentLoadedEventEnd事件開始到loadEventEnd事件結束,計算公式:loadEventEnd-DomContentLoadedEventEnd
|
JS錯誤率 |
出現JS錯誤的比例。JS錯誤包含Javascript錯誤程式碼和位置資訊。
|
服務端響應時間 |
伺服器響應時間是指應用伺服器處理請求所消耗的時間,即應用響應時間,等於請求到達應用伺服器到應用程式碼執行完成並輸出響應資訊的時間。(需要透過Server探針自動注入方式嵌碼,否則伺服器響應時間為零)
|
AJAX請求響應時間 |
所有Ajax請求時間在時間軸的投影合併的總耗時
|
unload |
解除安裝當前頁面的耗時,計算公式:unloadEnd-unloadStart
|
Redirect |
頁面重定向操作所消耗的時間,計算公式:redirectEnd-redirectStart
|
Cache |
取快取資料的耗時,計算公式:domainLookupStart-fetchStart
|
DNS |
透過域名解析服務(DNS),將指定的域名解析成IP地址的消耗時間。
|
TCP建連時間 |
瀏覽器和WEB伺服器建立TCP/IP連線的消耗時間。當元素下載完成後,瀏覽器可能會根據伺服器返回的結果保持此連線,而不是完全關閉此連線。當監測節點再次和相同的伺服器建立連線時,會複用此連線,對應消耗時間可能為0。此指標即為TCP/IP連線三次握手的前二次握手的時間(從IE傳送TCP包SYN到收到伺服器返回的TCP包SYN ACK的時間),第三次握手時間(從IE傳送TCP包ACK到伺服器接收此TCP包的時間)不計算在內。
|
排隊時間 |
排隊時間指伺服器端的請求阻塞時間,即請求從Web前端伺服器(例如Apache, nginx或F5負載均衡裝置)到達應用服務端的時間。
|
首包時間 |
從開始頁面請求到瀏覽器開始接收到HTML程式碼的時間,不包括排隊時間和伺服器端的時間,計算公式:responseStart-connectEnd -排隊時間-伺服器響應時間
|
剩餘包時間 |
從responseStart事件開始到responseEnd事件結束,計算公式:responseEnd-responseStart
|
首次渲染時間 |
從導航到頁面首次渲染消耗的時間,計算公式:firstPaintTime-navigationStart(又名:白屏時間,firstPaintTime)
|
首次互動時間 |
從使用者的第一個動作發生時間 – navigationStart,其中動作包括:點選,按鍵,滾動滑鼠。
|
自定義載入時間(使用者可感知時間)
|
每個頁面都可以設定一個使用者自定義的載入時間效能指標。計算方式為路由切換時間至主動呼叫sdk ready方法時間差值
|
AJAX平響時間 |
平均每次AJAX請求的響應時間
|
AJAX傳輸資料量 |
單位KB,平均每次AJAX請求的資料傳輸量(上傳+下載位元組數)
|
AJAX回撥時間 |
平均每次AJAX請求的回撥時間(回撥時間是指當資料從伺服器傳到客戶端之後,原生代碼呼叫這些資料做相應的處理,可以理解為本地執行時間)
|
客戶端時間 |
從請求某資源到下載完過程中,沒有出現網路傳輸的時間片段之和,比如DNS-TCP建連,之間的切換需要消耗CPU來排程,這就可能會產生很短的時間空隙
|
事件平均響應時間 |
操作請求完成時間。
|
統計方式,透過performance API
https://www.w3.org/TR/navigation-timing/
Core Web Vitals(https://web.dev/vitals/)
核心指標
metrics
|
描述
|
含義
|
TTFB
|
time to first byte
|
從請求到資料返回第一個位元組所消耗時間
|
TTI
|
Time to Interactive (TTI)
|
DOM樹構建完畢,可繫結事件
|
DCL
|
DOMContentLoaded
|
HTML文件完全載入解析完成
|
L
|
onLoad
|
依賴資源全部載入完畢
|
FP
|
first paint
|
第一個畫素點繪製完成時間
|
FCP
|
First contentful paint (FCP)
|
首次繪製非空白節點時間
|
FMP
|
first meaningful paint
|
首次有意義繪製(需要自定義)
|
LCP
|
lLargest contentful paint (LCP)
|
在視口中最大的頁面元素載入時間
|
FID
|
First input delay (FID)
|
使用者首次和頁面互動到頁面響應的時間
https://github.com/GoogleChromeLabs/first-input-delay
|
CLS
|
Cumulative layout shift (CLS)
|
度量在頁面開始載入到其生命週期狀態更改為隱藏之間發生的所有意外佈局更改的累積分數
|
使用谷歌Web Vitals進行獲取
https://github.com/GoogleChrome/web-vitals
interface Metric { // The name of the metric (in acronym form). name: 'CLS' | 'FCP' | 'FID' | 'LCP' | 'TTFB'; // The current value of the metric. value: number; // The delta between the current value and the last-reported value. // On the first report, `delta` and `value` will always be the same. delta: number; // A unique ID representing this particular metric that's specific to the // current page. This ID can be used by an analytics tool to dedupe // multiple values sent for the same metric, or to group multiple deltas // together and calculate a total. id: string; // Any performance entries used in the metric value calculation. // Note, entries will be added to the array as the value changes. entries: (PerformanceEntry | FirstInputPolyfillEntry | NavigationTimingPolyfillEntry)[]; }
(3)前後端呼叫打通
-
pageGuid,重新定義pageGuid生成規則,暫定為 router+orgId+timestamp+random ? 進行加密處理
-
requestId,由網路庫統一生成,生成方式參考BFE_logId生成規則,在請求header中攜帶,用於skywalking tid進行對映
-
operateId,需要使用者明確【操作】起止時機,並手動呼叫SKD API。
1) 在使用方初始化【操作】時,呼叫 SDK.initOpt('操作名'),該方法返回本次操作operateName,用於後續傳遞;
1) 在使用方明確【操作】開始時,呼叫SDK.startOpt('operateName'),SDK生成唯一operateId,存於localStorage.pageGuid..operateName.operateId(示例);
2) 為了保證上下文獨立性,在該操作涉及到的請求options中配置{operateName: 'operateName'},網路庫在傳送請求時,主動獲取operateId並攜帶,取值方式為:localStorage.pageGuid..operateName.operateId || ‘’;
3) 在使用方明確【操作】結束時,需呼叫SDK.endOperate(操作名);SDK將進行該次操作相關操作時間,頁面渲染時長等統計,儲存在localStorage.pageGuid.bucket中,透過上報策略時機進行統一上報。
(4)錯誤日誌報警
現有sentry在採集錯誤日誌時,增加pageGuid,requestId,operateId相關資訊,透過(3)對映方式與Skywalking 呼叫鏈進行鏈路打通。
鑑別有用報錯資訊分類,確定報警閾值及報警人配置,進行HI,郵件,簡訊,電話報警方式;增加報警升級策略。
增強報表展示能力,透過pageGuid等維度檢視頁面級別報錯,以便於進行下一步排查和跟進。
2、上報
直接複用神策SDK上報機制。
1.上報時機(備選)
-
頁面載入和重新重新整理 -
頁面切換路由
-
儲存於localStorage.pageGuid.bucket中; -
bucket中數量超過閾值,觸發上報; -
頁面解除安裝unload,觸發上報;
2.上報方式
如何上報效能資料,我們第一反應就是透過ajax請求的形式來上報前端效能資料。這種方法有一些缺陷,比如必須對跨域做特殊處理以及如果頁面銷燬後,相應的ajax方法並不一定傳送成功等問題。
其中跨域的問題比較好處理,最難解決的問題是 如果頁面銷燬,那麼對應的ajax方法並不一定能成功傳送。
根據google analytics(GA)中的方法,根據瀏覽器的相容性以及url的長度,來採用不同的方法上報效能資料:
透過動態建立img標籤的方式,在img.src中拼接url的方式傳送請求,不存在跨域限制。如果url太長,則才用sendBeacon的方式傳送請求,如果sendBeacon方法不相容,則傳送ajax post同步請求。
(1)、sendBeacon方法
解決在文件解除安裝或者頁面關閉後無法完成非同步ajax請求的問題,很多情況下我們會把非同步變成同步。在頁面解除安裝的unload或者beforeunload事件中執行同步方法呼叫。
但是同步方法呼叫存在一個問題,就是會推遲A頁面切換進入B頁面的時間。而sendBeacon方法解決了該問題,簡單來說:
sendBeacon方法在頁面銷燬期,可以非同步的傳送資料,因此不會造成類似同步ajax請求那樣的阻塞問題,也不會影響下一個頁面的渲染
sendBeacon的呼叫方式為:
function sendBeacon(url,data){ //判斷支不支援navigator.sendBeacon let headers = { type: 'application/x-www-form-urlencoded' }; let blob = new Blob([JSON.stringify(data)], headers); navigator.sendBeacon(url,blob); }
(2)動態建立img標籤的形式
透過動態建立img標籤的形式,指定src屬性所指定的url來傳送請求,首先不受跨域的限制,其次img標籤動態插入,會延遲頁面的解除安裝保證圖片的插入,因此可以保證在頁面的銷燬期,請求可以發生。
function imgReport(url, data) { if (!url || !data) { return; } let image = document.createElement('img'); let items = []; items = JSON.Parse(data); let name = 'img_' + (+new Date()); image.onload = image.onerror = function () { }; let newUrl = url + (url.indexOf('?') < 0 ? '?' : '&') + items.join('&'); image.src = newUrl; }
(3)同步ajax post請求
動態建立img標籤的方法,拼接url的時候存在一定的問題,因為瀏覽器對url的長度是有限制的。而sendBeacon方法相容性不是很好,最後兜底的處理方式就是傳送同步的ajax請求,同步的ajax請求前面說過,會在頁面銷燬期之前執行,雖然會有一定程度的阻塞下一個頁面的渲染。
function xmlLoadData(url,data) { var client = new XMLHttpRequest(); client.open("POST", url,false); client.setRequestHeader("Content-Type", "application/json; charset=utf-8"); client.send(JSON.stringify(data)); }
(4)綜合解決方案
首先拼接攜帶引數的完整的url,判斷url的長度,如果url的長度小於瀏覽器允許的最大長度內,那麼透過動態建立img標籤的形式來傳送前端效能資料,如果url太長,則判斷瀏覽器是否支援sendBeacon方法,如果支援,則透過sendBeacon方法來傳送請求,否則傳送同步的ajax請求。
function dealWithUrl(url,appId){ let times = performanceInfo(appId); let items = decoupling(times); let urlLength = (url + (url.indexOf('?') < 0 ? '?' : '&') + items.join('&')).length; if(urlLength<2083){ imgReport(url,times); }else if(navigator.sendBeacon){ sendBeacon(url,times); }else{ xmlLoadData(url,times); } }
3、方案設計(Hybrid H5)
3.1總體架構
3.2.上報流程
3.3.流程時序圖
4.服務端採集及分析
搭建OAP服務,進行資料清洗加工及展示