基於JMeter的效能壓測平臺實現

這篇文章已經是兩年前寫的,短短兩年時間,JMeter開源應用技術的發展已經是翻天覆地,最初由github開源專案zyanycall / stressTestPlatform 形成的這款測試工具也開始慢慢沉寂,畢竟決定效能測試發展的始終是人不是工具。不過基於JMeter的開源技術還是富有生命力的,也是我熱於去專研的,儘管這個社會正在拋棄我們這些大齡的技術愛好者。

我個人日常喜歡用這款開源測試工具,因這它屬於輕量級的JMeter效能壓測平臺,不同於MeterSphere那種重量級的開源測試平臺,畢竟對於工具的選擇是多元化的,介面測試或指令碼開發時我們可以直接用免安裝的JMeter,中小規模的壓測專案我們就可以選擇這款壓測平臺工具(因為部署快捷,使用方便,適合小公司的專案),更大的專案就需要規模化的平臺化工具了,比如雲壓測平臺。

以下是我梳理的該測試平臺架構圖:

基於JMeter的效能壓測平臺實現

JMeter壓測平臺

該平臺已經開源: https://gitee。com/smooth00/stressTestSystem,繼承於zyanycall/stressTestPlatform的開源專案;Docker版部署方案 https://gitee。com/smooth00/stressTestSystemDocker

針對小白,提供了一鍵部署包:https://gitee。com/smooth00/stressTestSystem/releases,只要安裝了JDK1。8,下載安裝包stressTestSystem。rar 解壓後,透過批處理指令碼就能執行壓測平臺。

一鍵部署包所對應的Docker映象可以直接pull:docker pull smooth00/stresstest-system

以下是主要的技術選型及說明:

核心框架:Spring Boot 1。5

安全框架:Apache Shiro 1。3

檢視框架:Spring MVC 4。3

持久層框架:MyBatis 3。3

定時器:Quartz 2。3

資料庫連線池:Druid 1。0 (阿里開源)

日誌管理:SLF4J 1。7、Log4j

頁面互動:Vue2。x(前後端未分離)

前端監控:ECharts 3。8

壓測核心(即JMeterEngine):Apache JMeter 5。1。1(現已適配

5.4.1

版本)

指令碼呼叫核心:Apache Commons Exec 1。3(棄用)

遠端執行命令:Ganymed build210

批次上傳元件:bootstrap-fileinput v4。5。2

JVM內部快取:Guava 18。0

選用的快速框架是輕量級的,而且是方便快速部署的:

【renren-fast開發框架】,具體可以上網獲取:renren-fast開發文件 - 人人開源效能測試平臺的專案結構:

stress-test├─doc 專案SQL語句││─lib 專案引用外部jar包(預設沒有)│├─common 公共模組│ ├─aspect 系統日誌│ ├─exception 異常處理│ ├─validator 後臺校驗│ └─xss XSS過濾│ ├─config 配置資訊│ ├─modules 功能模組│ ├─api API介面模組(APP呼叫)│ ├─job 定時任務模組│ ├─oss 檔案服務模組│ ├─sys 許可權模組│ └─test 壓測模組│ ├─RenrenApplication 專案啟動類│ ├──resources │ ├─mapper SQL對應的XML檔案│ ├─static 第三方庫、外掛等靜態資源│ ├─views 專案靜態頁面│ └─application。yml 環境配置

平臺已實現的部分功能:

(1)用例管理:

基於JMeter的效能壓測平臺實現

用例管理

用例管理支援jmx指令碼的上傳和引數化檔案及測試附件的上傳,一個用例建立一個目錄(指令碼、引數檔案、附件、測試報告都在同一用例下儲存)。刪除用例時會自動刪除用例下所關聯的指令碼,並一併刪除已同步到各個節點的檔案。

(2)指令碼檔案管理

每個指令碼具有啟動和停止壓測執行緒的功能(具有狀態標識),每個引數化檔案或附件具有同步到各個節點的功能(同步完成後標識為同步成功)。

基於JMeter的效能壓測平臺實現

指令碼檔案管理

支援指令碼檔案線上修改(基於jmx檔案的模板化編輯):

基於JMeter的效能壓測平臺實現

JMX編輯

啟動指令碼可以選擇指定節點壓測,只要空閒狀態的節點都可以選擇,真正實現並行任務執行:

基於JMeter的效能壓測平臺實現

指令碼啟停

指令碼檔案除了啟動和停止功能,還能配置是否開啟報告生成和是否開啟前端監控,監控是echarts圖形監控,如下:

基於JMeter的效能壓測平臺實現

監控檢視

呼叫指令碼進行壓測的方法分為兩種:

壓測模式

呼叫模組

特點

優缺點

指令碼呼叫模式

Apache Commons Exec

相當於透過遠端執行jmeter命令呼叫指令碼

優點:

實現簡單,無需過多程式設計;

缺點:

無法多執行緒控制,無法開啟echarts監控

引擎呼叫模式

JMeter Engine

用的是Jmeter壓測核心,透過Jmeter內部方法啟停測試

優點:

更輕量級,多執行緒控制,能echarts監控,方便擴充套件;

缺點:

需要依賴更多程式設計實現

另外支援將指令碼新增到任務,用的是框架本身的任務管理,加上cron表示式生成器外掛的應用,可以方便的實現指令碼的定時任務建立,這樣就能定時執行指令碼(這個是LR所不具備的功能,一般可以用於介面的自動化測試):

基於JMeter的效能壓測平臺實現

任務排程

(3)測試報告管理

基於JMeter的效能壓測平臺實現

測試報告管理

報告生成模式

呼叫模組

特點

優缺點

Jmeter Home命令模式

Generate Report ByScript

透過執行jmeter命令呼叫報告

優點:

實現簡單,無需特別程式設計,直接呼叫;

缺點:

無法多執行緒控制,無法多使用者併發生成報告,完全依賴本地Jmeter_Home目錄的配置

Web程序多執行緒模式

Generate Report Local

採用本地web程序來實現CSV報告檔案轉化成html模板報告

優點:

更加輕量級,多執行緒控制,使用更加輕便,配置簡單(不依賴於Jmeter Home的配置);

缺點:

需要依賴更多程式設計實現

預設執行指令碼過程中,生成了CSV報告,透過【生成報告】按鈕,觸發將csv報告轉換成html DashBoard(這一步也是透過Commons Exec排程jmeter命令完成),展示效果如下:

基於JMeter的效能壓測平臺實現

除了測試報告,還支援除錯報告(顯示介面請求資訊,類似於Jmeter的檢視結果樹) ,原理是呼叫xls模板將JTL結果轉為html報告(區別於測試報告是將CSV結果轉為html報告),展示效果如下:

基於JMeter的效能壓測平臺實現

介面報告

(4)分散式節點管理

基於JMeter的效能壓測平臺實現

分散式節點管理

分散式節點管理透過Ganymed遠端執行linux命令,來啟動或是停止各節點的Jmeter-server,啟動命令格式如下:

//啟動節點 String enableResult = ssh2Util。runCommand( “cd ” + slave。getHomeDir() + “/bin/testCases/” + “\n” + “sh ” + “。。/jmeter-server -Djava。rmi。server。hostname=”+slave。getIp());

如果是禁用節點,就是透過遠端執行殺程序的命令:

ssh2Util。runCommand(“ps -efww|grep -w ‘jmeter-server’|grep -v grep|cut -c 9-15|xargs kill -9”);

這種方式挺方便,省了在多臺linux節點機上,手動去連線和啟動jmeter(分佈節點越多越顯得方便快捷)。而且節點管理支援節點權重控制(原理是基於程序修改執行緒組的執行緒數屬性來實現)。

另外跟原來相比,分散式節點管理增加了校準功能,就是為了解決節點因為人為因素停了,而管理端不能及時的作出判斷,現在透過校準可以將後臺節點的程序狀態跟前臺同步一次(避免程序異常關閉或錯誤啟動),目前不是自動校準。因為無論是實時監聽埠還是定時校準,效率都不是最好的。以後可以嘗試在壓測過程中新增監聽機制,來實時監測節點狀態,而非壓測時段就透過手動點選校準即可,這樣會相對經濟一些。

(5)監控擴充套件(Grafana+InfluxDB)

由於我在以前的一篇文章中寫過有關Grafana+InfluxDB與Jmeter的監控(關於Jmeter長時間壓測的視覺化監控報告),可以直接拿過來整合使用。整合的方式是開啟Grafana的匿名登入(在defaults。ini中配置),到官網下一個Jmeter的監控檢視JSON模板匯入,同時以跳轉的方式將Grafana嵌入到平臺的iframe中。

var URL_IP = parent。location。host;var URL_PORT = parent。location。port;window。location = “http://”+URL_IP。replace(“:”+URL_PORT,“”)+“:3000/d/joulMbxmz/apache-jmeter-dashboard?orgId=1”;

基於JMeter的效能壓測平臺實現

Grafana

另外可以將Grafana和InfluxDB及一鍵啟動指令碼與效能壓測平臺一起部署,實現在部署層面上進行整合和無縫對接使用。

寫到這我們的效能壓測平臺前期部分基本介紹完了,還有些功能未開始開發,比如像阿里雲PTS的壓測場景配置,這比較複雜,相當於是把指令碼的場景設定移到WEB介面上,另外還要結合定時器進行指令碼的靈活排程(發起壓測、結束壓測、持續時間、測試周期等),目前來看還沒想好怎麼實現。但是可以先實現執行緒組的線上管理:

(6)執行緒組管理

基於JMeter的效能壓測平臺實現

執行緒組管理

執行緒組管理的原理也不復雜,就是上傳指令碼時,透過dom4j遞迴掃描Jmx指令碼(本質上是xml)的節點,獲取執行緒組的配置節點引數,儲存入庫,然後在介面上修改和管理,改完還可以同步回Jmx指令碼(也是透過dom4j對xml進行set配置)。目前實現的管理的執行緒組類別包括預設的ThreadGroup、jp@gc - Stepping Thread Group、jp@gc - Ultimate Thread Group,這三種已經算最常用的了。執行緒組管理的目的就是免去簡單的執行緒配置(如併發數配置、執行緒組禁用)還要線下設定,設定完又要上傳,另外指令碼的執行緒組配置在平臺介面上也能一目瞭然,避免又要臨時開啟Jmeter工具進行檢視。

(7)壓測節點監控

【該功能未開源】

基於JMeter的效能壓測平臺實現

既然有了分散式節點管理,那麼我們也可以做到對節點的分散式監控,在Influxdb-Grafana的基礎上,引入telegraf來實現;我們透過介面上,加入監控開啟和停止的按鈕,再結合節點上部署的一些批處理命令,來實現一鍵啟動監控代理。

啟動和關閉的核心程式碼如下:

/** * 批次切換節點的監控狀態 */ @Override public void updateMonitorBatchStatus(List slaveIds, Integer monitorStatus) { String execStr=“”; for (Long slaveId : slaveIds) { StressTestSlaveEntity slave = queryObject(slaveId); // 本機節點無需遠端操作 if (“127。0。0。1”。equals(slave。getIp()。trim())) { Runtime r = Runtime。getRuntime(); //執行本地作業系統命令,不關心返回結果, Process p = null; try { if(OS_NAME_LC。startsWith(“windows”)){ if (StressTestUtils。ENABLE。equals(monitorStatus)) execStr = “cmd。exe /c \”“+StressTestUtils。getJmeterHome()+”\\telegraf\\start。cmd\“ -a”; else execStr = “cmd。exe /c taskkill /im telegraf。exe /f”; }else{ if (StressTestUtils。ENABLE。equals(monitorStatus)) execStr = “sh ”+StressTestUtils。getJmeterHome()+“/telegraf/startUp。sh ”+slave。getSlaveName(); else execStr = “sh ”+StressTestUtils。getJmeterHome()+“/telegraf/stop。sh ”+slave。getSlaveName(); } p = r。exec(execStr); p。waitFor(); } catch (Exception e) { // TODO Auto-generated catch block e。printStackTrace(); } finally { if(null!=p){ p。destroy(); p=null; } } //更新資料庫 slave。setMonitorStatus(monitorStatus); update(slave); continue; } //其他節點需要SSH遠端連線 SSH2Utils ssh2Util = new SSH2Utils(slave。getIp(), slave。getUserName(), slave。getPasswd(), Integer。parseInt(slave。getSshPort())); // 避免跨系統的問題,遠端由於都時linux伺服器,則檔案分隔符統一為/,不然同步檔案會報錯。 String telegrafServer = slave。getHomeDir() + “/telegraf/telegraf”; String md5Str = ssh2Util。runCommand(“md5sum ” + telegrafServer + “ | cut -d ‘ ’ -f1”); if (!checkMD5(md5Str)) { throw new RRException(slave。getSlaveName() + “ 監控模組出錯!找不到telegraf啟動檔案!”); } //如果是開啟監控 if (StressTestUtils。ENABLE。equals(monitorStatus)) { //啟動監控 execStr = “sh ” + slave。getHomeDir() + “/telegraf/startUp。sh ” + slave。getSlaveName()+“ ”+stressTestUtils。getLocalIp(); }else{ //禁用監控 execStr = “sh ” + slave。getHomeDir() + “/telegraf/stop。sh”; } String enableResult = ssh2Util。runCommand(execStr); logger。error(enableResult); if (!enableResult。contains(“telegraf”)) { throw new RRException(slave。getSlaveName() + “ telegraf執行失敗!請先嚐試在節點機命令執行”); } //更新資料庫 slave。setMonitorStatus(monitorStatus); update(slave); } }

從程式碼我們也可以看出,我們將telegraf啟動和配置檔案都放置在jmeter節點的根目錄下,這樣能方便遠端SSH呼叫,同時我們將監控平臺的IP和節點名稱也傳送過去,並更新到telegraf。conf檔案中,這樣啟動的telegraf程序就會將監控資料發回到我們的influxdb,並透過grafana監控到。以下是監控的效果:

基於JMeter的效能壓測平臺實現

Grafana

這樣我們就實現了對壓測機的監控(在測試過程中不對壓測機監控是不合理的,特別是CPU、記憶體、網路IO等,萬一出現測試機的效能瓶頸由於不能及時發現就會導致無用功),除了壓測機的監控,我們可以由點及面,做出一個壓測平臺的監控服務平臺,對被測服務端也進行監控。

(8)平臺日誌監控

【該功能未開源】

基於JMeter的效能壓測平臺實現

說是日誌監控,目前只是實現了日誌的前臺展現功能,將info、debug、warn、error不同的日誌級別用顏色標識。獲取日誌的方式是開通websocket通道,用spring-boot推送實時日誌到前端頁面顯示,這種方式其實也沒什麼特別的,在網上能找到大把的實現方式,但其作用還是挺大的,平臺執行或是壓測過程中如果出現異常,直接在前臺跟蹤日誌即可,沒必要再跑到後臺伺服器上用tail命令跟蹤了,易用性變強了。

(9)支援redis快取壓測資料並生成報告

【該功能未開源】

基於JMeter的效能壓測平臺實現

透過redis快取壓測的監聽資料,並自動生成報告(並套用自定義的報告模板),在速度上要遠快於csv生成html報告,主要是為了解決測試報告檔案太大生成報告太慢的問題(利用redis叢集可以加大測試資料快取的併發性),另一個好處是將自己需要的測試結果快取到redis中,可以方便自定義更多元化的報告展現形式,提升了平臺的擴充套件性(該功能不對外開源)。

(10)支援動態改變指令碼全域性變數

【該功能未開源】

透過在壓測節點機或主節點開啟 beanshell。server。port=9000

基於JMeter的效能壓測平臺實現

首先在jmeter指令碼中使用全域性變數來控制執行緒數或吞吐量,如下:

基於JMeter的效能壓測平臺實現

然後執行指令碼,這樣在介面就有個【調參】按鈕,點選就可以動態的修改引數,達到動態控制:

基於JMeter的效能壓測平臺實現

調完引數並提交後,可以看到執行緒數被動態的改變為10:

基於JMeter的效能壓測平臺實現

我們經常在壓測過程中會發現壓力太大或是壓力不夠,我們不需要停止指令碼重來,只需要動態的改變壓力,繼續測試,這就是動態調參的魅力。一般用於動態調整吞吐量或都動態調整執行緒數。具體原理可以參考我們的另一篇文章:Jmeter動態吞吐量實現_smooth的部落格-CSDN部落格_jmeter 目標吞吐量

注:未開源的功能是我為公司內部定製化開發,以後考慮開源,畢竟不是什麼複雜的程式碼。

版權宣告:本文為博主原創文章,轉載請附上博文連結!https://blog。csdn。net/smooth00/article/details/83380879