為什麼需要一個前端監控系統
通常在一個大型的 Web 專案中有很多監控,比如後端的服務 API 監控,介面存活、呼叫、延遲等監控,這些一般都用來監控後臺介面資料層面的資訊。而且對於大型網站系統來說,從後端服務到前臺展示會有很多層:內網 VIP、CDN 等。但是這些監控並不能準確地反應使用者看到的前端頁面狀態,比如:頁面第三方系統資料呼叫失敗,模組載入異常,資料不正確,空白開天窗等。這時候就需要從前端 DOM 展示的角度去分析和收集使用者真正看到的東西,從而檢測出頁面是否出現異常問題
需要監控系統解決的問題
頁面通常出現以下問題時需要使用郵件、簡訊通知相關人員修復問題
- 狀態碼返回錯誤(50x, 40x)無法開啟
- 模組載入失敗
- 頁面亂碼
- 資料正確性
觸發報警時要有現場快照,以便復現問題
技術選型
監控的意義和迴歸測試的在本質上是一致的,都是對已上線功能進行迴歸測試,但不同的是監控需要做長期的可持續可迴圈的迴歸測試,而測試僅僅需要在上線之後做一次迴歸
既然監控和測試的本質一致,那我們完全可以採用測試的方式來做監控系統。在自動化測試技術遍地開花的時代,不乏很多好用的自動化工具,我們只需要把這些自動化工具進行整合為我們所用即可
- NodeJS – 特別適用於網路密集型任務
- PhantomJS – 模擬無介面的瀏覽器,提供豐富的核心互動 API
NodeJS
NodeJS 是一個 JavaScript 執行環境,非阻塞 I/O 和非同步、事件驅動,這幾點對於我們構建基於 DOM 元素的監控是非常重要的
PhantomJS
PhantomJS 是一個基於 webkit 的瀏覽器引擎,可以使用 JavaScript API 來模擬瀏覽器的操作。它使用 QtWebKit 作為它的瀏覽器核心,使用 webkit 來編譯解釋執行 JavaScript 程式碼。也就是說任何你可以在 webkit 瀏覽器裡做的事情,它都能做到
它不僅是個隱形的瀏覽器,提供了諸如 CSS 選擇器、支援 Web 標準、DOM 操作、JSON、HTML5、Canvas、SVG 等,同時也提供了處理檔案 I/O 的操作等。PhantomJS 的用處可謂非常廣泛,諸如網路監測、網頁截圖、無瀏覽器的 Web 測試、頁面訪問自動化等
為什麼不是 Selenium
做自動化測試的同學肯定都知道 Selenium。可以使用 Selenium 將測試用例在瀏覽器中執行,而且 Selenium 對各種平臺和常見瀏覽器支援比較好,但是 Selenium 上手難度係數略高,而且使用Selenium 需要在伺服器端安裝瀏覽器
考慮到監控主要任務在監控不在測試。系統並不需要太多考慮相容性,而且監控功能相對單一,主要對頁面進行功能上的迴歸測試,所以選擇了 PhantomJS
架構設計
架構概覽
架構簡述
對於 DOM 監控服務,在應用層面上進行了垂直劃分:
- 規則管理系統
- 規則佇列生成器
- 長時持續處理器
- PhantomJS 服務
- 服務化 API
在應用層面上進行的垂直劃分可以對應用做分散式部署,提高處理能力。後期也方便做效能優化、系統改造擴充套件等
解決方案
前臺規則錄入
這是一個獨立的 Web 系統,系統主要用來收集使用者錄入的頁面資訊、頁面對應的規則、展示錯誤資訊。通過呼叫後端頁面抓取服務來完成頁面檢測的任務,系統可以建立三種型別的檢測頁面:常規監控、高階監控、可用性監控
常規監控
錄入一個頁面地址,和若干檢測規則。注意這裡的檢測規則,我們把常用的一些檢測點抽象成了一條類似測試用例的語句。每條規則用來匹配頁面上的一個 DOM 元素,用 DOM 元素的屬性來和預期做匹配,如果匹配失敗系統就會產生一條錯誤資訊,後續交由報警系統處理
匹配型別 一般有這麼幾種:長度、文字、HTML、屬性
處理器 類似程式語言中的操作符:大於、大於等於、小於、小於等於、等於、正則
這樣做的處就是,錄入規則的人只要瞭解一點 DOM 選擇器的知識就可以上手操作了,在我們內部通常是交由測試工程師統一完成規則的錄入
高階監控
主要用來提供高階頁面測試的功能,一般由有經驗的工程師來撰寫測試用例。這個測試用例寫起來會有一些學習成本,但是可以模擬 Web 頁面操作,如:點選、滑鼠移動等事件從而做到精確捕捉頁面資訊
可用性監控
可用性監控側重於對頁面的可訪問性、內容正確性等比較 嚴重的問題 做即時監控。通常這類頁面我們只需要在程式裡面啟一個 Worker 不斷的去獲取頁面 HTML 就可以對結果進行檢測匹配了,所以我們選擇了 NodeJS 來做非同步的頁面抓取佇列,高效快速的完成這種網路密集型任務
主動錯誤上報
頁面指令碼執行錯誤監控
頁面引入一段監控指令碼來收集頁面產成 error 事件返回的錯誤資訊,自動上報給後端服務,在系統裡面可以彙總所有報錯資訊,以及對應的客戶端瀏覽器版本、作業系統、IP 地址等
頁面主動上報
這個功能需要對應的前端工程師在程式碼中呼叫錯誤上報 API,來主動提交錯誤資訊。主要使用的場景有,頁面非同步服務延時無響應、模組降級兜底主動通知等。監控指令碼提供幾個簡單的 API 來完成這項任務
1 2 3 4 5 6 7 |
// error 方法呼叫後立即上報錯誤資訊併發出郵件、簡訊通知 errorTracker.error('錯誤描述') // info 方法呼叫後立即上報資訊,並在單位時間內僅產生一條郵件、簡訊通知 errorTracker.info('資訊描述') // log 方法呼叫後由報錯檢測是否達到設定閥值,最終確認是否報錯 errorTracker.log('日誌資訊') |
後端頁面抓取服務
由於京東很多頁面內容是非同步載入的,像首頁、單品等系統有許多第三方非同步介面呼叫,使用後端程式抓取到的頁面資料是同步的,並不能取到動態的 JavaScript 渲染的內容,所以就必須使用像 PhantomJS 這種能模擬瀏覽器的工具
常規監控我們使用 PhantomJS 模擬瀏覽器開啟頁面進行抓取,然後將監控規則解析成 JavaScript 程式碼片段執行並收集結果
高階監控我們使用 PhantomJS 開啟頁面後向頁面注入像 jasmine, mocha 等類似的前端 JavaScript 測試框架,然後在頁面執行對應的錄入測試用例並返回結果
規則佇列生成器
規則佇列生成器會將採集的規則轉化類成訊息佇列,然後交由長時持續處理器一次處理
為什麼採用類訊息佇列的處理方式?
這和 PhantomJS 的效能是密不可分的,由多次實踐發現,PhantomJS 並不能很好地進行併發處理,當併發過多,會導致 CPU 過載,從而導致機器當機
在本機環境下的虛擬機器中進行併發測試,資料並不理想,極限基本在 ab -n 100 -c 50 左右。 所以為了防止併發導致的問題,就選擇了使用類訊息佇列來避免因為併發過高導致的服務不可用
類訊息佇列的實現
我們這裡通過呼叫內部的分散式快取系統生成類訊息佇列,佇列的生成其實可以參考資料結構–佇列。最基本的模型就是在快取中建立一個 KEY ,然後根據佇列資料結構的模式進行資料的插入和讀取
當然,類訊息佇列的中間介質可根據你實際的條件來選擇,你也可以使用本機記憶體實現。這可能會導致應用和類訊息佇列競爭記憶體
長時持續處理器
長時持續處理器是要功能就是消費規則佇列生成器生成的類訊息佇列
長時持續處理實現
在長時持續處理器的具體實現中,我們利用了 JavaScript 的 setInterval 方法來持續獲取累訊息佇列的內容下發給規則轉化器,然後轉發給負載均衡排程器。之後再對返回的結果進行統一處理,比如郵件或者簡訊報警
API
PhantomJS 服務可以做為公共 API 提供給客戶端進行測試需求的處理, API 通過 HTTP 方式呼叫。在 API 的處理上需要提供 HTTP 資料到規則和 PhantomJS 的轉換。從而又演化出了 HTTP 資料到規則轉換器
PhantomJS 服務
PhantomJS 服務是指將 PhantomJS 結合 HTTP 服務和子程式進行服務化的處理
首先、啟動 HTTP 服務,然後將長時處理器下發的規則進行進一步轉化,轉化後啟動子程式,HTTP 服務會監聽子程式的處理結果,並在處理完畢之後返回
報警系統
報警系統我們目前使用的是京東內部自己的統一監控平臺 UMP,通過呼叫平臺提供的一些 API 來實現報警郵件與簡訊通知
如何根據報警定位到具體頁面?
使用者通過監控管理系統錄入規則後,監控系統會根據 UMP 規則針對使用者錄入的頁面生成 UMP 使用的 key。當長時持續處理器發現 PhantomJS 服務返回的結果標示為異常後,就會使用 key 來進行日誌記錄
何時出發報警?
報警主要分為了簡訊和郵件報警。郵件報警是在每條異常之後就會發給指定系統使用者。簡訊則是根據異常次數來進行處理的,當異常次數過大,就會下發簡訊通知
部署
對於系統部署可以分為兩大塊進行。因為機器資源數量有限,沒有將所有部分都單獨部署
規則管理系統以及規則佇列生成器和持續處理器整合部署在一臺機器上,PhantomJS 服務部署在了其他的機器上。程式管理使用了著名的 NPM 模組 —— PM2
PM2 是一個帶有負載均衡功能的 NodeJS 應用的程式管理器。可充分利用 CPU,並保證程式穩定存活
PM2 特性:
- 內建負載均衡(使用 Node cluster 叢集模組)
- 無縫重啟類似 nginx reload
- 具有 Ubuntu 和 CentOS 的開機啟動指令碼
- 控制檯檢測
不過在目前部署任務中,並沒有使用內建負載均衡的特性,沒用通過叢集的方式部署代理。僅使用了後臺執行和無縫重啟的特性
總結與展望
其實我們現在開發的這套監控系統並不複雜,只是合理的運用了一些現有的技術框架。抽象出來我們自己需要的一些功能。但卻有效的達到了我們的預期功能,並且節省了很多之前需要人肉測試的時間成本。系統本身還有很多問題在待解決狀態,比如報警系統的規則處理與閥值設定,JavaScript 報錯的準確過濾機制等,這些問題我們都會逐個解決,並且未來的前端監控系統會成為一個平臺,核心服務在後端爬取頁面服務,應用端可以有多種形式,比如監控、測試工具等
一些可以持續優化點:
- 監控系統雖然在應用層面進行了垂直劃分,但是由於機器資源等限制,並沒有進行單獨功能的部署。這點可能會在後期的使用中進行優化
- PhantomJS 服務還需要進一步優化,以承載大併發,大處理量。提供穩定的服務
- 報警由於依賴於公司內部的 UMP 系統,所以並不是特別靈活,後期可以考慮自己實現一套報警機制
部落格原文同步:https://keelii.github.io