有贊全鏈路壓測引擎的設計與實現

有贊技術發表於2018-12-24

一年以前,有贊準備在雙十一到來之前對系統進行一次效能摸底,以便提前發現並解決系統潛在效能問題,好讓系統在雙十一期間可以從容應對劇增的流量。工欲善其事,必先利其器,我們拿什麼工具來壓測呢?我們做了很多前期調研和論證,最終決定基於 Gatling 開發有讚自己的分散式全鏈路壓測引擎 —— MAXIM。一年多來,我們使用 Maxim 對系統做了很多次的效能壓測,在提升系統效能、穩定性的同時,也得益於歷次壓測的實踐經驗逐步改進 Maxim。


1、前期調研

1.1、技術選型的核心考量

由於時間或成本關係,我們打算基於開源軟體做二次開發,而以下就是我們技術選型時的核心考量:

  • 將請求編排成業務場景
    以使用者下單這個場景為例,使用者完成一筆訂單,可能需要開啟商品主頁-加入購物車-選擇收貨地址-下單支付這些步驟,而串起這一系列的請求就是所謂的將請求編排成業務場景
  • 流量控制
    流量控制可以是縱向的,如上述下單場景中,各個步驟的請求量逐漸減少,整體呈現一個漏斗模型;也可以是橫向的,比如使用者正在瀏覽 A 商品的商品詳情頁,然後看到了 B 商品的推薦,轉而瀏覽 B 商品的商品詳情頁
  • 壓力控制
    指壓測時併發使用者數、吞吐量(RPS / TPS)的控制
  • 資料跟請求引數的繫結
    壓測往往涉及大量的測試資料,而如何繫結資料和請求引數是我們需要考量的
  • 對分散式測試的支援
    因為是全鏈路壓測,自然需要多臺施壓機共同協作施壓,自然而然的需要分散式支援
  • 測試報告
    良好的測試報告是我們分析效能問題的必備條件
  • 二次開發的成本
    由於時間或人力關係,我們也需要考慮二次開發成本

1.2、4 個主流開源效能測試框架對比

我們調研了以下 4 個主流開源效能測試框架:

有贊全鏈路壓測引擎的設計與實現

  • ApacheBench
    Apache 伺服器自帶,簡單易用,但不支援場景編排、不支援分散式,二次開發難度較大
  • JMeter
    JMeter 支援上述很多特性,如分散式、良好的壓測報告等,但其基於 GUI 的使用方式,使得當我們的壓測場景非常複雜幷包含很多請求時,使用上不夠靈活;此外在流量控制方面的支援也一般
  • nGrinder
    基於 Grinder 二次開發的開源專案,支援分散式,測試報告良好,但和 JMeter 一樣,在場景編排和流量控制方面支援一般
  • Gatling
    支援場景編排、流量控制、壓力控制,測試報告良好,且提供了強大的 DSL(領域特定語言)方便編寫壓測指令碼,但不支援分散式,且使用 Scala 開發,有一定開發成本

以上,我們最終選擇基於 Gatling 做二次開發。

2、Maxim 新增的特性

Maxim 在 Gatling 基礎上開發了很多新特性:

  • 支援分散式
    一個控制中心(Control Center,負責排程) + 多個壓力注入器(指施壓機)
  • 提供 GUI,並對使用者隱藏壓測過程的複雜性
    高效地建立、執行(手動/定期)測試任務
  • 管理測試資源
    測試資源包括壓測指令碼、資料集(為壓測請求提供測試資料,由資料塊構成的一個集合,資料塊是大量測試資料的最小分割單元)、壓力注入器
  • 支援壓測指令碼引數化
    Maxim 中併發使用者數、RPS、持續時間等都可以通過 GUI 動態注入壓測指令碼
  • 支援壓力注入器系統狀態監控
    實時監控壓力注入器的 CPU、記憶體、I/O 等指標
  • 自動生成壓測報告,保留歷史壓測報告
    採集多個壓力注入器的壓測日誌,自動彙總生成壓測報告,並保留歷史壓測報告

3、Maxim 的技術架構

3.1、Maxim 的總體架構

有贊全鏈路壓測引擎的設計與實現

Maxim 架構的主要構成:

  • Maxim Console
    Maxim Console 主要銜接 GUI 和 Maxim Control Center,負責建立、執行測試任務,接收壓力控制引數等
  • Maxim Control Center
    Maxim 的控制中心,這裡主要負責壓測任務的排程、讀取資料集、上傳指令碼和資料以及讀取日誌並生成壓測報告
  • Load Injector Cluster
    壓力注入器叢集,主要分為 Agent 和 Gatling 兩部分,Agent 負責接收 Maxim 控制中心的排程指令以及向控制中心反饋本壓力注入器壓測情況,而 Gatling 則是真正發起壓測請求的地方,並將壓測日誌寫入 InfluxDB
  • Data Factory
    壓測資料首先會在大資料平臺通過 MapReduce 任務生成,而資料工廠負責為控制中心讀取這些資料並返回資料集
  • Cloud Storage
    雲端儲存,Maxim 控制中心會將壓測指令碼和壓測資料上傳到雲端儲存,當 Agent 收到控制中心的任務執行指令時,會從雲端儲存下載壓測指令碼和對應的資料塊。設計雲端儲存的目的主要是為了模擬真實使用者環境在公網發起壓測請求,但有贊目前都是從內網發起壓測請求,所以雲端儲存的功能也可以以其他方式實現,比如 Agent 直接從大資料平臺下載資料集
  • InfluxDB
    所有壓力注入器產生的日誌都會統一寫入 InfluxDB,方便生成壓測報告

Maxim的排程演算法
控制中心會根據當前測試任務使用的壓力注入器數量,將資料集中的資料塊平均分配給每個壓力注入器,讓每個壓力注入器只下載對應的那些資料塊。此外,併發使用者數、RPS 也會被平均切分給每個壓力注入器。這樣,每個壓力注入器的負載基本是一致的。

3.2、Maxim 的領域抽象

有贊全鏈路壓測引擎的設計與實現

  • TestJob - JobExecution - JobSliceExecution
    當壓測任務開始執行,首先會在控制中心生成 JobExecution,監控本次壓測任務的整體執行狀態。控制中心又會根據上述排程演算法為每個壓力注入器生成任務分片 JobSliceExecution 並下發到各個壓力注入器,其中包含了指令碼、資料集等資訊
  • TestScript
    壓測指令碼
  • DataSet和DataChunk
    資料集和組成資料集的資料塊單元,目前單次壓測任務已支援多資料集,為多個場景提供不同的壓測資料,即混合場景壓測
  • LoadProfile
    從 GUI 接收動態引數,主要包括壓力注入器數量、併發使用者數、RPS、持續時間等

有贊全鏈路壓測引擎的設計與實現

  • ExecPlan
    執行計劃,包括按需執行和週期執行兩種執行方式
  • ExecutionStatus
    關於狀態機下一節會詳細介紹

3.3、Maxim的狀態機

有贊全鏈路壓測引擎的設計與實現

Maxim 狀態機是 Maxim 分散式的核心,控制中心和各個 Agent 的行為都受狀態機變化的影響。 建立任務並開始執行以後,各個任務分片(JobSliceExecution)首先會進入 preparing 狀態,各個 Agent 會從雲端儲存下載壓測指令碼和各自對應的那些資料塊,下載完成後再將這些資料塊合併成一個 Json 資料檔案作為壓測指令碼的資料輸入。如果下載失敗則會重試,即 Prepare。如果所有 Agent 都成功下載了指令碼和資料,則各個 JobSliceExecution 會相繼進入 prepared 狀態,等所有 JobSliceExecution 進入 prepared 狀態後,JobExecution 也會進入 prepared 狀態,並向各個 Agent 發起執行指令,各個 JobSliceExecution 進入 running 狀態,等所有 Agent 執行完成且各個 JobSliceExecution 變成 completed 狀態之後,JobExecution 也會進入 completed 狀態,此時壓測任務執行完成並生成壓測報告。如果各個任務分片在 preparing、prepared 或 running 過程中有任何一個出錯,則出錯的分片會進入 failed 狀態並通知控制中心,控制中心則控制其他分片中止正在執行的任務並進入 Stopping 狀態,等這些分片中止成功並都變成 stopped 狀態後,JobExecution 會被置成 failed 狀態。當然了,也可以手動停止壓測任務,這時候 JobSliceExecution 和 JobExecution 都會被置成 stopping->stopped 狀態。

3.4、Maxim 控制中心的技術架構

有贊全鏈路壓測引擎的設計與實現

Maxim 控制中心採用六邊形架構(也叫埠與介面卡模式),核心服務只處理核心業務邏輯(如排程演算法),其他功能如與 Agent 通訊、指令碼儲存、資料儲存、壓測報告等都是通過適配層呼叫特定實現的 API 實現。具體技術的話,與 Agent 通訊使用 grpc 實現,其他功能則是通過 SPI 技術實現,我們把這一層叫做接縫層(Seam)。這樣設計最大層度的解耦了核心業務邏輯和其他功能的特定實現,我們在保持接縫層 API 不變的情況下,可以自由選擇技術方案實現相應的功能。比如資料服務這塊強依賴了有讚的大資料平臺,假設我們開源了 Maxim,外部團隊就可以選擇他們自己的技術方案實現資料服務,或者為了測試目的 Mock 掉。

4、改造 Gatling

原生 Gatling 是將壓測日誌寫入本地日誌檔案的,而在分散式中,如果每個壓力注入器都把日誌寫在本地,則為了基於所有日誌分析生成壓測報告,我們需要首先收集分散在各個壓力注入器中的日誌檔案,這樣顯然是低效的。所以我們改造了 Gatling ,將所有日誌都寫到同一個 InfluxDB 資料庫。需要生成壓測報告時,控制中心從 InfluxDB 資料庫讀入本次壓測任務的所有壓測日誌並儲存為一個日誌檔案,再交由 Gatling 的日誌處理模組來生成壓測報告。

5、擴充套件 Gatling

原生 Gatling 不支援 Dubbo 壓測,所以我們擴充套件 Gatling,實現並開源了 gatling-dubbo壓測外掛,具體實現方法詳見 Dubbo壓測外掛的實現——基於Gatling

6、Maxim 的未來展望

Maxim 目前還是個單打獨鬥的產品,未來我們希望與大資料平臺、運維平臺等系統打通,讓 Maxim 逐漸進化為一個一站式的壓測平臺,並引入更多新特性,如壓測過程和壓測報告的實時計算和展示等等。

我的系列部落格
混沌工程 - 軟體系統高可用、彈性化的必由之路
非同步系統的兩種測試方法
Dubbo壓測外掛的實現——基於Gatling

我的其他測試相關開源專案
捉蟲記:方便產品、開發、測試三方協同自測的管理工具
gatling-dubbo:擴充套件自Gatling的Dubbo效能測試外掛

招聘
有贊測試組在持續招人中,大量崗位空缺,只要你來,就能幫你點亮全棧開發技能樹,有意向換工作的同學可以發簡歷到 sunjun【@】youzan.com

有贊全鏈路壓測引擎的設計與實現

相關文章