1. 程式人生 > >餓了麽全鏈路壓測平臺的實現與原理

餓了麽全鏈路壓測平臺的實現與原理

test www. 試用 推送 定位 用戶操作 吞吐量 查詢 定期清理

背景

在上篇文章中,我們曾介紹過餓了麽的全鏈路壓測的探索與實踐,重點是業務模型的梳理與數據模型的構建,在形成腳本之後需要人工觸發執行並分析數據和排查問題,整個過程實踐下來主要還存在以下問題:

  1. 測試成本較高,幾乎每個環節都需要人力支撐,費時費力。
  2. 由於測試用例較多,涉及的測試機範圍較廣,手工執行容易犯錯,線上測試尤其危險。
  3. 記錄結果和測試報告極不方便,需要二次加工、填寫和上傳。
  4. 測試過程中靠手工監控,覆蓋不全且定位問題困難。

基於這些因素,我們決定推進全鏈路壓測的自動化進程。這篇我們主要介紹全鏈路壓測平臺的實踐。

目標

為了解決以上核心痛點,平臺至少需要保證以下方面的功能:

  • 用例管理:用戶建立測試用例,上傳資源文件,系統進行分類管理。
  • 壓測執行:一鍵觸發已有測試用例,可指定線程數、預熱時間、測試周期和測試機等,可以自動切分數據,分布式執行。
  • 實時結果(熱數據):響應時間、吞吐量、錯誤率等數據以圖表形式實時顯示。
  • 測試結果(冷數據):平均響應時間、平均吞吐量,90/95/99線等數據以圖表形式在測試結束後顯示。
  • 測試機集群監控:監控測試集群的使用狀態,提示用戶可用的測試機。
  • 安全保障:平臺應該對用戶操作進行適當限制,並能自我應對一些異常情況。

主要功能與實現概要

壓測平臺是典型的B/S類型Java Web項目,基於Spring Boot開發,前端使用AngularJS。平臺本身不執行測試只做調度,避免成為瓶頸,後臺均使用JMeter執行測試;平臺自身會維護壓測機集群,保證壓測機是可供測試的;測試期間產生的冷數據(用例數據、結果數據)持久化至MongoDB,熱數據(實時數據)持久化至InfluxDB並定期清理。

技術分享圖片

分布式測試

在使用JMeter進行性能測試時,如果並發量比較大,單機的配置可能無法支持,這時需要聯合多機進行分布式測試。我們並沒有采用JMeter自身的分布式功能,而是自己做了實現,主要是考慮到:

  1. JMeter的分布式測試執行和單機執行方式的差異較大,這會導致平臺架構不必要的復雜度,實際用戶只感知測試機的數量區別。
  2. JMeter分布式執行的方式,master機通常不參與測試,而是收集slave信息,但這會造成一定程度上的資源浪費。

我們使用餓了麽內部的EOC工具對壓測機進行調度,並實現了JMeter自身具備的分布式調度功能,相應的對比見下表。基於我們的實現,壓測過程中熱數據和冷數據是分離傳輸的,冷數據在測試完成後才會傳輸(如果不需要壓測端數據,甚至可以配置不存儲冷數據),因此該方案對於擴容是非常友好的,理論上支持的TPS沒有上限。

技術分享圖片

測試狀態流轉

測試狀態流轉是壓測平臺的核心,每一輪正常的測試工作都會經歷一條主線,即:配置 -> 觸發 -> 運行 -> 結果收集 -> 清理。測試狀態流轉的設計圍繞著這條主線,輔以外部幹預和內部監控功能,保證測試的正常進行。

此外,我們還需要鑒別出各種可能的異常情況(如測試觸發失敗)和合理情況(如用戶主動停止),並據此輸出不同的反饋信息,並且無論測試流程出現何種分支,最後都能形成閉環,這對系統的健壯性非常重要。以下是狀態流轉圖:

技術分享圖片

舉兩個典型的應用場景:

  1. 用戶觸發測試後,由於測試壓力過大,運維要求立刻停止測試,這時的閉環為:初始 -> BOOTING -> LAUNCHING -> 用戶觸發停止 -> 直接進入收集流程 -> 狀態標記為STOP -> 清理壓測機 -> 初始
  2. 用戶觸發測試後,壓測機由於某些原因突然斷網,這時的閉環為:初始 -> BOOTING -> LAUNCHING -> 監控發現問題 -> 狀態標記為FAILURE -> 清理壓測機 -> 初始

整個狀態流轉的實現,采用異步Job機制實現了類似狀態機的概念,狀態屬性持久化到數據庫中,便於恢復。

實時數據

JMeter本身並不提供圖形化的實時數據展示功能,以往我們只能通過JMeter Log看到一些粗略的信息,並結合外部監控工具觀察指標情況。在壓測平臺中,我們對該功能進行了實現,主要原理是通過JMeter的Backend Listener (JMeter 3.2+),將測試結果實時發往InfluxDB,同時平臺向InfluxDB輪詢查詢數據,得到實時曲線並展示給用戶。

技術分享圖片

在實踐過程中,向InfluxDB發送數據的頻次是比較高的,可能會對壓測機造成壓力,因此我們改造了JMeter的InfluxDB sender,替換了HTTP方式,增加了以UDP協議發送數據的實現,解決了這一問題。

預配置

在“測試狀態流轉”一節中,闡明了測試的總體流程,第一步即為配置,配置的具體內容是:將測試需要的腳本、數據文件和插件,推送到每一臺測試機上,為測試執行做好準備。

但如果測試文件比較大,或者需要配置的壓測機數量比較多,配置可能會占用較多時間,影響測試進程,這是很多平臺遇到的共性問題。對此,我們提出了預配置的概念,即用戶可以提前對測試用例,針對某幾臺壓測機進行配置工作,但並不執行。預配置會保留一定的時間,在這段時間內,用戶可以直接執行測試,不需要再重復配置。

技術分享圖片

以下為預配置的狀態轉換圖:

技術分享圖片

熔斷與兜底

全鏈路壓測一般都在線上真實環境進行,安全是首要考慮的因素,不能因為測試本身而導致服務不可用或事故。我們提供了四個維度的機制進行安全保障。

  1. 權限管理:用戶權限分級管理,不能隨意觸發他人的測試用例,同時高峰期和禁止發布期,不允許執行任何測試。
  2. 停止功能:這是面向用戶的手動停止功能,用戶可以隨時點擊運行狀態下的測試用例上的停止按鈕,後臺會直接kill掉所有運行該測試用例的測試機上的JMeter進程。
  3. 熔斷功能:系統會根據實時信息中的錯誤率進行判斷,當一定時間內的實時錯誤率達到或超過某個閾值時,該次測試將被自動熔斷,無需用戶幹預。
  4. 兜底腳本:最極端的情況,當整個系統不可用,而此時需要停止測試時,我們提供了一份外部腳本直接進行停止。

結果收集

由於我們自己實現了JMeter分布式的管理,因此我們也需要自己對結果集進行處理,結果的主要來源為測試生成的JTL文件。

針對JTL,結果數據需要做預聚合再存入,原因是JTL中單條結果數據的大小非常小(大約100多個字節),但總量很大(可能有幾萬到幾百萬條),很容易由於重復存儲維度字段的KEY值而導致表過大。預聚合主要根據以label作大分類,維度作小分類,以時間作為聚合標準,interval固定,從而保證Document大小不會過大。

以下是結果集片段的數據結構概要(單label):

技術分享圖片

前端會根據持久化的數據,形成可視化圖表,為用戶展現。

總結與展望

全鏈路壓測平臺自今年7月上線以來,為超過5個部門累計提供了上千次測試服務。按照每一類測試配置和執行的人力成本為15分鐘計算,大約節省了1000個小時的工作量。

目前,全鏈路壓測的自動化只是針對測試執行範疇,我們還有很多工作要做,在未來,我們希望能夠將自動化的腳步覆蓋到測試前和測試後,真正建設出全鏈路壓測的自動化生態體系。

參考:https://www.testwo.com/article/1104

餓了麽全鏈路壓測平臺的實現與原理