2017前端監控系統探索總結

美團點評點餐發表於2019-03-01

背景

在當下網際網路行業,監控概念與重要性已經不需要再進行闡述,然而監控分為多種,對物理層(機房,雲主機)的監控,對傳輸鏈路的監控,對已部署服務的監控等等,而後端的程式碼通常直接執行在伺服器並處於24小時實時的監控狀態之下,一旦服務的可用性出現問題,SRE和DEV往往在第一時間就會收到告警,並根據告警資訊在第一時間解決故障。相比之下,前端程式碼則執行在客戶端上,為了讓前端能夠和後端一樣,需要將客戶端的前端程式碼監控起來,當客戶端出現故障時,能第一時間通知到前端負責人,定位故障,及時止損。

前端監控系統都需要監控什麼?在前端應用日漸複雜的今天,我認為對於前端的監控主要分為三個方面:

效能監控

為什麼要監控效能呢?因為對於任何一家網際網路公司,效能往往與利益直接相關。有資料調查顯示:當Google 延遲 400ms時,搜尋量下降 0.59%、Bing 延遲 2s,收入下降 4.3%、Yahoo 延遲 400ms,流量下降 5-9%,所以,很多公司在做使用者體驗分析時,第一個看的就是效能監控指標,在前端領域,效能無非是以下參考指標:

  • 白屏時間;
  • 首屏時間;
  • 使用者可互動時間;
  • 總下載時間;
  • TCP連線時間;
  • HTTP請求時間;
  • HTTP響應時間;

而且很重要的一點,也是大家往往最容易忽視的:效能會伴隨產品的迭代而有所衰減。特別在移動端,網路條件十分不穩定的情況下。效能優化不存在“黃金法則”,我們需要一套效能監控系統持續監控、評估、預警頁面效能狀況、發現瓶頸,更有針對性的指導優化工作的進行。

異常監控

除了效能之外,我們還要監控客戶端指令碼發生的報錯,前端報錯受網路,機型,業務邏輯影響而且大部分錯誤難以還原現場,比如我們團隊時時收到使用者的反饋和投訴:

  • 甲使用者 : 點選XX按鈕之後頁面白屏了 
  • 乙使用者:  優惠劵消費後,支付金額顯示不正確
  • 丙使用者:最近頁面特別卡,點選tab好久才能反應過來

面對使用者的反饋,開發經常感到困惑:到底有多卡,哪個步驟卡?是個別現象還是大面積都受到了影響?白屏時頁面請求的返回碼是多少?是被執行商劫持還是CDN出了問題?能讓使用者用Charles配合抓個包麼?如何做有針對性的優化?優化的結果怎麼去衡量?

為了解決這些痛點,我們需要對客戶端服務進行基於使用者行為的監控。

資料監控

網際網路公司的產品,每一個決策,每一個迭代都需要分析各種資料,資料中往往會有我們需要的答案:

  1. 頁面PV,UV可能直接影響轉化率
  2. 從使用者訪問頁面的順序挖掘使用習慣(等等)

這部分監控資料主要供PM/PD使用,業務資料可以驅動業務自身的增長,有人曾說:“要降低創業失敗的可能性只有兩種方法:一是未卜先知,另一個是做精益的資料分析”,由此可見資料分析的重要性。

傳統解決方案及缺陷

目前已經存在了一些針對前端監控解決方案:Sentry,Badjs,jsTracker,GrowingIo等等,在公司內部也有自研的監控系統。它們都從不同維度試著解決前端在監控方面的問題,大家的實現思路都很類似、要實現監控,首先要採集指標:

效能指標

這裡要針對的主要是白屏時間、首屏時間、使用者可操作、總下載時間。

這裡以首屏時間為例:高版本chrome瀏覽器中可以直接通過 firstPaintTime 介面來獲取load time,但大部分瀏覽器並不支援,必須想其他辦法來監測。謹記一點,在做時間相關測量時,千萬不要使用setTimeout和setInterval方法,因為在單執行緒執行引擎中,非同步佇列的執行是不能確保執行時間的。這邊給出一種可行的測量方案,準確率在99成以上。

<doctype html>
<html>
    <head>
        <script type="text/javascript">
            var timerStart = Date.now();
        </script>
        <!-- 載入其他資源,執行程式碼blabla -->
    </head>
    <body>
        <!-- 路由框架掛載節點 -->

        <script type="text/javascript">
             $(document).ready(function() {
                 console.log("DOMready 時間 ", Date.now()-timerStart);
             });
             $(window).load(function() {
                 console.log("所有資源載入完成 時間: ", Date.now()-timerStart);
             });
        </script>
    </body>
</html>

複製程式碼

另一種優雅的解決方案是直接使用window.performance介面:

connectEnd                 Time when server connection is finished.
connectStart               Time just before server connection begins.
domComplete                Time just before document readiness completes.
domContentLoadedEventEnd   Time after DOMContentLoaded event completes.
domContentLoadedEventStart Time just before DOMContentLoaded starts.
domInteractive             Time just before readiness set to interactive.
domLoading                 Time just before readiness set to loading.
domainLookupEnd            Time after domain name lookup.
domainLookupStart          Time just before domain name lookup.
fetchStart                 Time when the resource starts being fetched.
loadEventEnd               Time when the load event is complete.
loadEventStart             Time just before the load event is fired.
navigationStart            Time after the previous document begins unload.
redirectCount              Number of redirects since the last non-redirect.
redirectEnd                Time after last redirect response ends.
redirectStart              Time of fetch that initiated a redirect.
requestStart               Time just before a server request.
responseEnd                Time after the end of a response or connection.
responseStart              Time just before the start of a response.
timing                     Reference to a performance timing object.
navigation                 Reference to performance navigation object.
performance                Reference to performance object for a window.
type                       Type of the last non-redirect navigation event.
unloadEventEnd             Time after the previous document is unloaded.
unloadEventStart           Time just before the unload event is fired.複製程式碼

介面相容性:

2017前端監控系統探索總結

異常指標

主動捕獲異常方案主要是 onError 和 addEventListener,onError 在 IE6 開始就支援了,所以 大部分系統的主動採集是使用的 onError。這裡注意瀏覽器的同源性策略(CORS),在高階瀏覽器中如果瀏覽器捕獲到了錯誤資訊,如果 JS 檔案所在的域名(如:meituan.com)和當前的頁面地址(如:dianping.com)是跨域的,那麼引擎會自動把onError 中的引數 替換為 script error,此時無法獲取行列數以及報錯詳細資訊。解決方案是在標籤引入時加上crossorigin欄位。

雖然傳統方法能夠自動catch大部分錯誤,但是也伴隨著以下缺陷

  1. 部分應用在不同網路,機型上表現不同,開發人員需要獲取到更詳細的分類資訊,傳統系統很難做分類聚合,開發面對幾十頁分類的table無從下手。
  2. 錯誤與錯誤之間往往成相關性,但是在傳統系統中catch到的錯誤都是孤立的,沒有基於使用者行為的分析。
  3. 針對異常的告警配置不夠靈活,無法滿足開發需求:大部分異常告警指標伴隨高低峰期波動,沒有一成不變的指標,而傳統告警平臺都是採用絕對閾值的告警方案,要麼高峰期誤報太多,要麼低峰期錯誤無法發現。

資料指標

傳統監控方案採用的都是手動埋點上報,但是缺點十分明顯:手動埋點往往會出現埋點混亂,甚至埋錯、漏埋的問題,埋點溝通過程中,資料團隊和業務工程團隊配合困難,新功能的開發往往伴隨著新埋點的增加,而且資料團隊的需求優先順序往往靠後,導致很多新上線功能得不到資料的驗證。

而有些基於關係型資料庫的系統實時性差,資料要隔天才能檢視,查詢命令執行一次動輒耗費幾十分鐘乃至上小時,已經沒有效率可言。

新解決方案

伴隨著上面討論的問題,我們尋求新的解決方案,一種高可用的監控方案。它應該具有如下特徵:

  1. 全量採集:監控指標健全,端到端採集全量的效能指標,關鍵執行方法,業務指標,覆蓋率做到2個9以上。
  2. 無需埋點:全量上報,無需開發人員手動埋點,從根本上杜絕錯埋和漏埋
  3. 查詢便捷:能夠按照地域,機型,作業系統,瀏覽器版本,網路狀況等多個維度進行資料查詢,最好支援全文搜尋,分類聚合。
  4. 場景還原:根據打點資訊,還原使用者從登陸開始一系列操作,建立基於使用者行為的時序圖。
  5. 實時性強:秒級查詢,上線後能立即看到優化效果並指導下一步優化。
  6. 智慧告警:採用靜態閾值和動態閾值結合的方式,兼顧高低峰,減少誤報漏報。並針對業務資料建立告警指標,發現系統深層次問題。

根據這些需求,我們團隊打造了一套全新的監控體系,新系統採用了無埋點SDK(小程式),ELK做本地化日誌儲存,並使用了基於動態閾值的告警策略。下面是系統架構圖:

2017前端監控系統探索總結

開發人員在本地通過程式碼接入SDK後,即可使用監控體系的全部功能:資料採集,上報,聚合分析,智慧告警等功能,而且所有資料均是實時上報,秒級查詢。

在最開始探索過程中,我們使用webpack外掛+npm包下載方式,但是由於兩部分上報邏輯在網路極差的情況下,會出現寫快取衝突的問題,導致重複上報或錯誤上報,現已將架構調整為單一script標籤引入的方式,部分保留下來的主動上報介面,開發可以根據自己需要在業務程式碼中再次封裝:

...
moduleClick(options) {
        const { name, ...otherOptions } = options;
        M.moduleClick(name, otherOptions);
    },
    /**
     * 曝光事件
     * @param options
     */
    moduleView(options) {
        const { name, ...otherOptions } = options;
        M.moduleView(name, otherOptions);
    },
    /**
     * 編輯事件
     * @param options
     */
    moduleEdit(options) {
        const { name, ...otherOptions } = options;
        M.moduleEdit(name, otherOptions);
    },
...

複製程式碼

 接入後,新版系統和之前相比有哪些變化?

1.因為採集是無埋點全量的,關鍵方法都會進行引數上報,然後可以通過分類聚合建立使用者的操作時序,通過故障上下文準確定位問題。

2017前端監控系統探索總結

2. 對resource和ajax請求指標做採集,可以篩選出故障使用者當時的場景資訊:

2017前端監控系統探索總結

3.告警採用動態閾值,對於週期性強的資料,通過機器學習的演算法進行環比告警,大大降低了誤報和漏報:

2017前端監控系統探索總結

在做日誌儲存的時候,資料量是一個挑戰,我們採用的是叢集架構,但是一個使用者量很大的站點,日誌的上報量是非常高的,高峰期時,一個1萬日活APP可能會突破3000的qps,這對日誌系統併發能力和穩定性是很大的挑戰。我們選擇了全量master+data節點的方式,對資料副本分片設定為1,任意一臺節點掛掉,會由副本選舉出新的主分片,不會造成日誌丟失。在寫入方面,我們選擇了bulk方式批量寫入,通過反覆試驗,批量寫入執行緒大小在5MB-15MB之間。由於日誌系統是主要面對寫入的,所以關閉了_all查詢,寫入效能優化1倍,同時gc新老分配為1:4,保證了批量寫入的穩定。

總結與未來規劃

通過對新監控解決方案的探索,我們積累了比較寶貴的資料分析經驗,對於終端哪些資料對於故障處理,效能優化起到重要作用有了新的認識,不過目前系統仍處於迭代過程中,距離預定的目標還有比較大的優化空間。

在未來,我們將重點攻克以下幾個問題:

1.系統輕量化

減少上報量:合併資料結構,釋放更多上行帶

優化SDK效能:減少快取寫入頻率,做到業務對監控模組無感

2.讓監控更智慧

識別周高峰和節假日,同時增強資料清洗能力,提高資料的可用性

3.優化資料分析體驗

開放埋點配置平臺,讓產品自主配置業務埋點,通過配置檔案轉化成埋點,省時高效。

相關文章