PhantomJS & NodeJS 在京東網站前端監控平臺的最佳實踐
為什么需要一個前端監控系統
通常在一個大型的 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 來完成這項任務
// 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/2016/11/17/best-practice-for-phantomjs-and-nodejs-at-jd-s-webdev-front-end-monitor-platform/