本文來自阿里雲前端監控團隊,轉載請註明出處
本文為2018年6月21日,在北京舉辦的GMTC(全球大前端技術大會),下午效能與監控專場,由阿里雲前端監控團隊前端技術專家彭偉春帶來的演講稿,現場反饋效果非常好,地上都坐了三圈,很多人反饋根本無法擠進去。先上現場照。
正文從這裡開始~
大家下午好,今天我給大家帶來的主題是《大前端時代前端監控的最佳實踐》
先做一個自我介紹,我叫彭偉春,英文名是Holden, 阿里花名是六猴, 大家都叫我猴哥。是阿里開源同構框架beidou的作者,目前是阿里雲前端系統技術負責人
今天我分享的內容分成三個部分
* 第一部分是“大前端時代前端監控新的變化”, 講述這些年來,前端監控一些新的視角以及最前沿的一些思考。
* 第二部分"前端監控的最佳實踐", 從使用的角度出發,介紹前端監控系統的各種使用姿勢
* 最後是“阿里雲ARMS前端監控系統架構”, 簡單地剖析下,阿里雲前端監控系統是怎麼實現的。
先進入我們第一個環節 大前端時代前端監控新的變化
要了解前端監控新的變化,還得先看看前端這些年發生了哪些變化
* 首先是Gmail的橫空出世,開啟了SPA的時代
* Backbone/Angular等框架帶來了MVVM模式的同時,也把JS從指令碼語言提升到了工程語言
* React Native/Weex把移動端開發從Hybrid模式進化到了跨端開發模式
* Node.js問世為前端帶來了更多的可能性
前端這些年發生了翻天覆地的變化,又會給監控帶來什麼呢?讓我們思考下以下幾個問題
* 傳統監控模式能否適用於新的技術?比如PV統計
* SPA模式下首屏如何計算?
* 跨端開發給監控帶來什麼什麼挑戰?
* 前端監控的上報模式在Node.js端是否合理?
* 接下來我和大家一起探討其中的一兩項
早些年,SPA如此盛行,我們也在業務中做了嘗試,體驗是大幅提升了,可業務方卻吐槽PV下降了
那到底是什麼導致了PV下降了呢?在後端直出時代,我們每一次的互動,都是向後端請求一個新的頁面,PV自然就高,改成SPA模式之後,大量的頁面請求變成了頁內路由,或者說是頁內轉場。那如何解呢?這難不倒我們,大部分框架路由都是基於雜湊實現的,我們只要偵聽hash改變,每次改變上報一次PV就好了。也有少量的路由並不是基於雜湊實現的,比如angular, 這時候就需要輕量級地hack pushState和replaceState
這樣就完美了嗎?
我們再思考下以下幾個案例
* 某新聞類的網站,每次看完之後,都會下拉重新整理,載入新的內容,這個時候是算一次PV還是多次?
* 天貓商品列表頁,看完一屏之後,向上滾動會再載入新的一屏,PV該算一次還是多次?
* 阿里雲郵後臺一直開著,每週上百次檢視,是算一個PV還是每次檢視都計算一次?
* 未關閉的瀏覽器tab幾小時之後再次瀏覽,該不該再計一次PV?
* 查詢資訊時,瀏覽器Tab之間快速切換,切換過程中要不要計一次PV?
其實還有很多其它層出不窮的場景,具體該如何去統計PV留給大家去思考, 不再展開
接下來我們探討一個大家最感興趣的話題: 效能。先看一組我們的統計資料,淘寶旺鋪頁面點選率隨載入時間變長從85%的點選率逐步降低到了82%,別小看這3%,在阿里這麼大的體量下,3%意味著巨大的商業價值,那站在前端監控的角度,首屏是如何統計出來的呢?
回到那個刀耕火種的年代,那時候要什麼沒什麼,都是自己動手豐衣足食。這就是手動打點階段: 手動打點,分別在頁頭和首屏dom節點處new Date()打點,計算差值,作為首屏時間,再加上setTimeout(new Date(), 0)標記首屏可互動時間
隨著前端的飛速發展,手工打點的模式早已滿足不了需求了。為了幫助開發人員更好地衡量和改進web效能,W3C效能小組引入了 Navigation Timing API 幫我們自動,精準的實現了效能測試的打點問題,大致地過一下,效能API裡面包含了【解除安裝上一個頁面】【重定向】【應用快取】【DNS域名解析】【TCP連線】【請求頁面】【響應】【頁面處理】最後觸發load事件,通常我們把domContentLoaded作為首屏時間。Chrome最早支援,IE跟進
在很長一段時間裡,我們都享受著performance API帶來的便利, 但隨著SPA模式的盛行,我們再回過頭來看看W3C標準是否足夠了。先來看一個案例,這是阿里雲某產品的管理後臺。整個載入過程分成三個部分,1. 載入初始的空殼頁面 2.載入JS資源並非同步請求資料 3. 前端渲染中間的主體部分。按照W3C標準取值首屏時間應該是1106ms, 而實際的首屏在1976ms,也就是完成非同步取資料後渲染完頁面的時間點。為什麼會相差如此大呢?實際上SPA的盛行讓W3C標準失去了原來的意義
針對這種情況Google lighthouse提出了FMP的概念,first meaning paint, 也就是主要內容可見時間,那什麼是主要內容? 每個人得出的結論可能會不一樣
先做一個猜想:主要內容 = 頁面渲染過中元素增量最大的點
先通過飛豬案例做一次驗證
猜想成立
再通過手淘案例做一次驗證
猜想不成立
那到底是什麼原因導致我們的猜想不成立?
* 首先是元素是否可見, 不可見的元素對使用者的影響基本為0
* 其次是每個元素對頁面的影響是否等效?由此引出權重,不同的元素採用不同的權重計算影響。阿里雲前端監控
根據上面的修正因子。我們重新設計了一遍演算法, 計算每次變化的得分,一起來看看,演算法是如何實現的?
如圖所示分為三個步驟
1. 偵聽頁面元素的變化
2. 遍歷每次新增的元素,並計算這些元素的得分總和
3. 如果元素可見,得分為 1 * weight(權重), 如果元素不可見,得分為0
如果每次都去遍歷新增元素並計算是否可見是非常消耗效能的。實際上採用的是深度優先演算法,如果子元素可見,那父元素可見,不再計算。 同樣的,如果最後一個元素可見,那前面的兄弟元素也可見。通過深度優先演算法,效能有了大幅的提升。
再拿之前的手淘案例來驗證一遍。
經過改良之後,第三屏主要內容的得分是最高的,符合預期。
那麼接下來首屏統計又會發生什麼樣的變化呢?其實統計首屏時間本身就是瀏覽器的職責,交由瀏覽器來處理是最好的。目前W3C關於首屏統計已經進入了提議階段,坐等W3C再次標準化。大家可以在github上看到最新進
限於篇幅,前端監控其它新的變化不再展開。講了這麼多前端監控的新變化,那什麼才是開啟前端監控最最正確地姿勢呢?
由此進入我們的第二個環節,“前端監控的最佳實踐”
我用一個表示式“要是什麼什麼就好了”來總結。我經常會想【要是天上能掉錢就好了】,【要是有個機器人幫我寫程式碼就好了】。同樣的,每次發版之後都是提心吊膽的,不知道使用者到底能不能正常使用。(這時候你就會想)要是能有雙眼睛幫我盯著系統就好了;每次出錯,都是使用者投訴反饋問題,實際等到使用者主動反饋問題,影響面已經非常大了: (這時候你就會想)要是能在第一時間發現錯誤就好了;
還真有這樣的案例,前年雙十一凌晨值班,突然收到郵件和簡訊告警,於是點開了詳情
發現在介面成功率趨勢圖中,介面請求量大幅上升,伴隨著成功率急劇下降,再檢視錯誤資訊聚合模組,發現頻率最高的錯誤資訊是【交易規則衝突】,深度排查之後,最終找出了原因,是運營配置的雙十一優惠規則和平時優惠規則產生了衝突,導致下單失敗。最後凌晨4點申請了緊急釋出修復了衝突,解除告警。
由此可以得出最佳實踐之一:主動監控。當然主動監控的內容不僅侷限於API成功率,也包括JS錯誤率等。稍微總結下流程:先是配置告警規則; 然後就可以放心大膽地睡覺了,如有任何風吹草動,系統馬上會通知到我們,再通過錯誤聚類模組,精準地定位問題。再手起刀落,bug修復完成。
再回到我們的【要是什麼什麼就好了】,在做效能優化的時候,有時候明明整體效能已經不錯了,可偏偏有少量使用者覺得很慢:(這時候你就會想)要是能知道慢速使用者發生了什麼就好了
這時候我們就需要用到【效能樣本分佈】,開啟頁面效能頁面,檢視0 -60秒之間每個區間的效能樣本分佈情況,從分佈圖中可以看出來大部分使用者載入時間都在2秒以內,極少數部分使用者的頁面時間在10秒左右的
拖動下面的滑塊,縮小時間範圍到10S左右,這時候系統就會篩選出10秒左右的慢會話
點選展開某次慢會話,不僅可以看到這次慢會話的基本資訊,比如網路型別等,還可以看到完整的資源載入瀑布圖,可以清晰地看出來,具體是什麼資源導致整個會話變慢。由此我們又可以得出最佳實踐之二:慢會話追蹤
再回到我們的【要是什麼什麼就好了】,有時候使用者提交了一條反饋,某某功能出錯用不了,這時候你又不知道使用者端到底報了什麼錯,是不是又得打電話給使用者,還得手把手教使用者如何通過瀏覽器開發者工具把錯誤截圖下來發你。我哩個去,使用者這個時候很可能因為系統太爛了,已經不堪其辱,早就把頁面關了並且發誓再也不用這破系統。(這時候你就會想)要是能知道使用者報了什麼錯就好了
別怕,開啟阿里雲前端監控的【訪問明細】搜尋使用者ID,直接可以看到該使用者在訪問過程中,到底報了什麼錯。
有時候拿到了使用者報錯時的基本資訊,也知道使用者報了什麼錯,但是在自己電腦上除錯的時候,無論如何也復現不了,這個時候是不是又得去和使用者溝通,讓使用者描述重現路徑,實際上使用者可能自己都忘了具體怎麼做才能重現錯誤。(這時候我們就會想)要是能重現使用者行為就好了。
【視訊演示】我們現場來模擬一次使用者出錯還原,左邊是使用者實際操作的螢幕,為了更好地展示效果,我把使用者行為實時地展示在右邊的螢幕上
* 第一步: 模擬使用者在淘寶頁面上做出了一系列的操作, 滑鼠移動、滾動頁面,搜尋等
* 第二步:假設突然出現了某某錯誤,這時系統會把記錄的使用者行為儲存到服務端
* 第三步: 開發人員通過會話ID查詢到出錯行為,最終進行還原。大家可以看到左邊螢幕不再操作,右邊螢幕還原出了之前出錯的所有行為。
大家一定在想這麼炫酷的能力是如何實現的呢?接下來就為大家揭祕阿里雲前端監控系統背後的技術架構。
就從大家最感興趣的錯誤還原講起,大家可能在猜測,是不是把整個頁面錄製成視訊了。其實不是這樣的,視訊太大了,不可能出錯了把一個視訊發到服務端,這樣是對使用者資源的嚴重浪費。先看示意圖(跟著箭頭從左到右)
* 首先,每一次會話都有一個唯一的session ID,這是串聯起所有行為的紐帶。
* 其次,使用者行為又分成兩個部分,其一是使用者的操作,比如滑鼠滑動,點選,頁面滾動等,其二是頁面的變化。這兩者我們都統稱為使用者行為,記錄在同一個佇列中。
* 一開始的時候,系統會記錄下初始的頁面作為第一幀,這是唯一的一次完整頁面記錄。
* 針對使用者操作,我們會記錄事件的型別,滑鼠位置等關鍵資訊,儲存到佇列中。
* 針對頁面變動,我們會起一個mutationObserve偵聽頁面的改動,每次只記錄改動的部分,儲存到佇列中。
* 無論是事件還是頁面改動,都是對等的一幀,每一幀都會有當前時間,與上一幀間隔時間等基本資訊使用者還原。
* 一旦出錯,SDK就把佇列傳送到監控系統,並清空當前佇列。
* 還原端根據記錄的行為佇列,根據時間逐一播放出來。最終形成一個類似於視訊的效果。
大家可能覺得還不過癮,接下來為大家講一下阿里雲ARMS前端監控系統的整體架構。
首先從左到右分成三個域。分別是日誌採集域,日誌分析域和監控告警域。在日誌採集域,客戶端通過SDK將資訊上報到Nginx伺服器, 日誌服務SLS在Nginx伺服器上起一個agent,去把日誌資訊同步過去,日誌到了SLS就相當於到了一個大的蓄水池。再通過實時計算引擎的計算,結果部分儲存到HBase,另一部分結果回存到SLS日誌服務用於搜尋。
最終通過restful API向前端提供資料,前端渲染出資料dashboard.
是不是感覺很簡單地樣子,有句話叫做【看山跑死馬】,山看起來就在眼前, 可就算騎馬過去馬都會累死。那就讓我們一起來揭開它的神祕面紗吧。
接下來重點介紹跟前端同學工作密切相關的日誌採集域,相比業界,我們的日誌採集還是有很多可圈可點之處的。比如說:
* 靜默採集: 只需要一行程式碼接入SDK就行了,所有的API請求、資源載入、JS錯誤、效能等都自動監控起來了。省去了繁瑣的配置。
* 單元測試 + 自動化測試:前端監控的目的就是去監控前端的異常情況,不給頁面帶來新的異常這是我們的底線,對此,我們有完善的單元測試和自動化測試去保障SDK本身的質量。
* (SDK出錯隔離):但實際上任何系統都不能保證自己不會出錯,那麼萬一SDK本身報錯了,我們還有異常隔離機制,確保出錯也不會影響業務的執行。
這些內容我都不詳細展開,那接下來我重點講一下,阿里雲前端監控是如何突破侷限優雅地上報日誌
大家都知道,http徵求意見稿rfc2616規定瀏覽器對於一個域名,同時只能有 2 個連線。而PV、UV、ajax請求、JS邏輯錯誤、頁面資源載入等等都會觸發上報,同時2個連線明顯不夠用,可能會造成網路阻塞,上報延遲
後來在修正稿rfc7230中去掉了這個限制, 只規定了限制數量,但並未指定具體數字,瀏覽器也實際放寬了限制。比如Chrome是同時6個連線。
然而,一個請求獨佔一個連線,有時候6個連線也是不夠用的
大家可能會想, 那既然規範都沒有指定要限制多少條,那瀏覽器為什麼還要限制6條呢?其實也是出於公平和安全考慮,如果不限制數量,理論上一個客戶端就能佔用大量伺服器資源,甚至壓垮伺服器。
那如何突破限制呢?有一個絕招:就是升級到http2, 利用h2的多路複用特性
一個連線上開啟多個流,還可以雙向資料傳輸,輕鬆突破6路並行限制。
* 思考一下:在http1時代的把資源雜湊在不同域名下還有效嗎?實際上非但不能提升效能,反而會新增連線開銷。
突破6路限制就夠了嗎?我們再來看看另一個很容易被忽略的部分:http頭部損耗。
* http請求中,每次請求都會包含一系列的請求頭來描述請求的資源和特性等。而頭部沒經過任何壓縮,每次請求都要佔用200-800個位元組,如果帶上一個比較大的cookie,甚至會超過1K,
* 而我們實際的日誌資料大小僅僅只有10 - 50位元組,頭部消耗佔了90%以上
* 另外,據Htpp Archive統計資料,平均每個頁面上百個請求,越來越多的流量消耗在頭部
* 最致命的是,UserAgent等資訊不會頻繁變動,每次請求都傳輸是一種嚴重的浪費。
再次利用h2頭部壓縮。先來看看採用h1和h2的效果對比。
h1下請求大小295 位元組, 而h2僅僅只有18 位元組,大小隻有區區16分之一,請求時間也從6ms降低到了4毫秒。
太神奇了,來快速地過一下http2頭部壓縮是如何實現的:
* 首先協議裡預設了一個靜態字典,用來表示常用的頭部欄位,比如圖中,2就是 method get. 以前需要把完整的key-value對發過去,現在只需要把一個數字發過去,大小大幅縮小。
* 其次,客戶端和服務端會共同維護一個動態表,動態表用來幹啥呢?舉個例子,比如useragent, 每個使用者的useragent值是不一樣的,沒法放到靜態表中去約定。但是對於同一個使用者會話,useragent是不會改變,這樣的值,就由客戶端和服務端協商決定存入動態表,這樣第一次傳輸過去之後,以後就只需要傳入動態表中的一個編碼就行了,圖中的62和63就是這樣的情況。連線中傳送的請求越多,就越能豐富動態表中的值,越到後面,請求效能越好(佐證了域名雜湊的方式不可取)
* 還有一類情況,值總是變來變去,也沒法儲存到動態表中。這時候,只能直接壓縮了。在h2中採用的是Huffman壓縮演算法,能把數字或字元最短壓縮到5個位元組,最大壓縮率是37.5%
其實除了頭部壓縮外,還有很多辦法減少體積,比如
* 採用http 204返回無響應體的response
* 採用post請求合併多條日誌,共用請求頭
* 錯誤呼叫堆疊中經常會出現很多的檔案url,佔了不少空間,可以考慮將他們抽取成一個變數
時間關係,日誌採集部分就到此為止。
接下來我們來看看一個監控系統最核心的部分:實時計算。
實時計算採用的是業界已經非常成熟的流計算,簡單地過一下概念。
這是一張表示流計算的經典結構圖,有兩種元件,水龍頭是spout,代表資料來源, 閃電是bolt, 代表處理邏輯。這裡面有兩個很重要的特徵。
* 其一是計算能力彈性,如果有更大的日誌量流入,能夠動態排程更多的算力來保障計算的實時性
* 其二是反壓。每個計算節點都可以根據自己的負載情況反壓上一級的計算節點,從而實現計算任務的更合理地分配。
思考一下:如何在海量日誌中實時取到限定條件的聚合資料?如圖所示,我想實時拿到【模擬頁面】在【廣東省】【最近24小時】【訪問速度】走勢
分析一下,如果需要畫出這樣的走勢圖,每個小時畫一個點,需要取24個點的值,每個節點寫個SQL把符合條件的資料求平均,
如果資料量很小的時候,取24次資料勉強效能上勉強可以忍受。
但是如果作為一個SASS系統,監控系統會接入非常多的專案,每時每刻都有大量的資料上報。系統也會積累海量的資料。取一個節點需要多少時間呢?參考離線計算大概要15分鐘, 24個節點,預估需要6個小時。這明顯是不可接受的。那阿里雲前端監控是如何做到實時拿資料的呢?
這就需要用到我們的大資料處理神器dataCube(資料立方),我們來剖析下資料立方是如何解決實時性的問題的。
如圖所示: 拿瀏覽器、裝置、地理區域三個維度為例,組成一個三維的資料立方。立方中的每個小格子代表一個聚合資料。
請看圖中數字3所在的格子,3代表三維,也就是Vivo裝置、chrome瀏覽器在北京地區的聚合量
再看一個黃色切面上的數字2,黃色切面代表瀏覽器維度的聚合,也就是上海地區Vivo裝置的聚合量,包括所有的瀏覽器。
再看最右下角的數字0代表0維,也就是所有的聚合量,包括所有的瀏覽器、所有的裝置、所有的地區。
資料立方的祕密就是把所有格子的值都預先計算出來,下次要取值,直接取資料立方的某個值就好了,本質上是一種空間換時間的思路。
看一個我們實際的處理場景,後設資料經過流計算之後,每分鐘、每小時、每天都會產生一個資料立方。而這個資料立方多達90多維。回到之前的案例,如果我想限定若干個條件拿到24小時趨勢圖,我只需要24個資料立方中把指定位置的小格子取出來就行了。計算時間就能大幅壓縮到秒級別。
【思考案例】資料立方本質上是把所有可能的組合提前算出結果,結果數量是一個笛卡爾積,如果某個維度的值非常多(比如淘寶商品詳情url中product id不斷變化, 導致url的值就有上千萬個), 直接導致維度爆炸, 該如何解?
由於時間限制,今天的主題就到此為止。有興趣的同學可以加我們的技術交流群,謝謝大家。
本文來自阿里雲前端監控團隊,轉載請註明出處