压测接口线程数设置_ZAT掌门性能压测巡检系统实战和落地
隨著業務拓展,對于接口性能的要求也在上升,各部門也開始針對部分慢接口進行優化,從測試角度針對這些優化需求進行測試時不僅要保證對應接口的功能正常使用同時也要驗證接口優化成果。在日常的開發工作中一些后臺服務配置的改動也會對接口的性能產生影響,我們急需要一套性能壓測巡檢平臺,對平時接口服務的技改,后端配置的優化進行持續的性能驗證。
項目日常痛點1.測試與開發都無法確定這次改動優化程度如何,與之前比較究竟提升多少性能。只能驗證相關功能正常。
2.測試不清楚開發優化接口用了那些方法需要觀察那些指標。
3.優化接口的改動與新增接口是否存在性能bug無法確定。
為了清楚的展示每次優化的成果,對新上線的接口檢查是否存在性能bug。基于目前的zat自動化平臺開發了性能壓測功能。
ZAT性能壓測巡檢實現方法1.ZAT性能巡檢平臺實現機制介紹
①平臺后臺使用jmeter執行自動化case本身就支持對于后端接口的性能壓測,通過對平臺的改造可以快速實現在平臺上對于接口性能壓測的功能。而不需要重新開發一套工具。
②性能壓測使用的case可以直接復制自動化case,修改幾個變量的配置可以直接用于性能壓測。節省編寫壓測用例的時間,同時使用自動化用例還可以模擬用戶使用場景。
③壓測結果通過平臺讀取直接存入數據庫,可以對多次性能壓測數據比對,接口優化時也可以通過歷史數據來比對是否有優化。
2.后期還需要解決的問題
Jmeter運行壓測時占用系統資源多,同時目前針對uat環境進行壓測大部分服務只有1臺機器,為了放置多部門壓測時對公共服務的調用影響到接口性能的數據。所以現在設置成同一時間只允許運行1個壓測job。其他端可以看到當前那個組在壓測狀態和進度。
之后會采取2種修改改進這個問題:
①采取分布式壓測,各組可以自己提供壓測機器通過slave的方式分擔壓測壓力。②等服務端接入docker后,通過各種策略將請求發送到對應容器。避免多個部門壓測同時調用同一個公共服務導致影響到性能數據ZAT性能壓測巡檢平臺實現架構圖ZAT性能壓測巡檢平臺流程圖目前zat自動化平臺后端使用jmeter來執行case。基于原有功能在之前的基礎上加入jmeter自身線程組與壓測所需的csv數據文件的設置進行改造,實現了壓測用例的生成,執行。通過對jtl文件中各性能數據的計算獲取接口相應時間,吞吐量,95線,99線等數據,同時調用grafana接口獲取壓測時對應服務器的內存,cpu和負載數據。匯總收集到的數據存入數據庫。用來跟歷史數據或者之后的數據進行比對。
ZAT性能壓測巡檢平臺的實現ZAT性能壓測巡檢平臺執行壓測的流程如下API信息設置壓測中需要關注對應的服務器的內存 CPU所以要在對應壓測case所使用的API信息中增加對應的APPID與需要壓測的IP地址
壓測Case的生成落地1.線程組設置
為了測試接口性能,需要模擬大量用戶發送請求的場景所以在自動化case的基礎上增加了對線程組數據的設置,通過這個設置可以模擬大量用戶訪問。
a.普通線程組(普通壓測設置)線程數代表起多少個線程發送請求;Ramp-up代表多少時間內發送完成;循環次數代表每個進程循環發送多少次;對應jmx模板實現代碼如下: 1{%?if?test_group.type?==??'normal'?%}
2????<ThreadGroup?guiclass="ThreadGroupGui"?testclass="ThreadGroup"?testname="{{?test_group.group_name?}}" 3?????????????????enabled="true">
4????????<stringProp?name="ThreadGroup.on_sample_error">continuestringProp>
5????????<elementProp?name="ThreadGroup.main_controller"?elementType="LoopController"?guiclass="LoopControlPanel" 6?????????????????????testclass="LoopController"?testname="Loop?Controller"?enabled="true">
7????????????<boolProp?name="LoopController.continue_forever">falseboolProp>
8????????????<stringProp?name="LoopController.loops">{{?test_group.loopTime?}}stringProp>
9????????elementProp>
10????????<stringProp?name="ThreadGroup.num_threads">{{?test_group.thread?}}stringProp>
11????????<stringProp?name="ThreadGroup.ramp_time">{{?test_group.LastTime?}}stringProp>
12????????<boolProp?name="ThreadGroup.scheduler">falseboolProp>
13????????<stringProp?name="ThreadGroup.duration">stringProp>
14????????<stringProp?name="ThreadGroup.delay">stringProp>
15????ThreadGroup>
16{%?endif?%}
b.梯度線程組(模擬梯度加壓)線程數代表總共起多少線程;持續時間代表啟動的線程總數達到最大值之后,再持續運行60秒;初始進程代表設置最開始時啟動多少個線程;上升梯度與時間代表每隔多少時間啟動多少進程,下降梯度與時間同理對應jmx模板實現如下: 1{%?if?test_group.type?==??'step'?%}
2????<kg.apc.jmeter.threads.SteppingThreadGroup?guiclass="kg.apc.jmeter.threads.SteppingThreadGroupGui" 3???????????????????????????????????????????????testclass="kg.apc.jmeter.threads.SteppingThreadGroup" 4???????????????????????????????????????????????testname="{{?test_group.group_name?}}"?enabled="true">
5????????<stringProp?name="ThreadGroup.on_sample_error">continuestringProp>
6????????<stringProp?name="ThreadGroup.num_threads">{{?test_group.thread?}}stringProp>
7????????<stringProp?name="Threads?initial?delay">0stringProp>
8????????<stringProp?name="Start?users?count">{{?test_group.start_thread?}}stringProp>
9????????<stringProp?name="Start?users?count?burst">{{?test_group.increase_thread?}}stringProp>
10????????<stringProp?name="Start?users?period">{{?test_group.period_time?}}stringProp>
11????????<stringProp?name="Stop?users?count">{{?test_group.decrease_thread?}}stringProp>
12????????<stringProp?name="Stop?users?period">{{?test_group.decrease_period_time?}}stringProp>
13????????<stringProp?name="flighttime">{{?test_group.last_time?}}stringProp>
14????????<stringProp?name="rampUp">stringProp>
15????????<elementProp?name="ThreadGroup.main_controller"?elementType="LoopController"?guiclass="LoopControlPanel"16?????????????????????testclass="LoopController"?testname="Loop?Controller"?enabled="true">
17????????????<boolProp?name="LoopController.continue_forever">falseboolProp>
18????????????<intProp?name="LoopController.loops">-1intProp>
19????????elementProp>
20????kg.apc.jmeter.threads.SteppingThreadGroup>
21{%?endif?%}
2.Csv文件設置(非必填)
考慮到許多接口的數據會存入redis,有時候使用固定的數據對接口進行壓測時接口直接從redis讀取數據無法模型正常用戶流程,所以通過綁定csv文件去切換變量(賬號,id等)模擬不同用戶發送請求
Csv數據設置
CSV文件會上傳到服務器抱錯 并且生成壓測用例時會綁定到對應計劃中變量名對應jmeter從csv文件中取值時對應列的數據對應的參數名稱用逗號分割
Csv存儲代碼實現
1try:
2????CsvFile?=?request.FILES.get("CsvFile")
3????Paraments?=?request.POST.get("paraments")
4????FilePath?=?rootPath?+?"/StressCsvStore/{}-{}-{}.csv".format(project.name,?alias,?case_id)
5????default_storage.save(FilePath,?ContentFile(CsvFile.read()))
6????CsvDb?=?StressCsvPath()
7????CsvDb.CsvPath?=?FilePath
8????CsvDb.Parement?=?Paraments
9????CsvDb.save()
10????CsvID?=?CsvDb.id
11except?Exception?as?e:
12????CsvID?=?0
Csv插件模板實現 1{%?if?test_group.CsvPath?!=??''?%}
2????<CSVDataSet?guiclass="TestBeanGUI"?testclass="CSVDataSet"?testname="CSV?Data?Set?Config"?enabled="true">
3??????<stringProp?name="delimiter">,stringProp>
4??????<stringProp?name="fileEncoding">stringProp>
5??????<stringProp?name="filename">{{test_group.CsvPath}}stringProp>
6??????<boolProp?name="ignoreFirstLine">falseboolProp>
7??????<boolProp?name="quotedData">falseboolProp>
8??????<boolProp?name="recycle">trueboolProp>
9??????<stringProp?name="shareMode">shareMode.allstringProp>
10??????<boolProp?name="stopThread">falseboolProp>
11??????<stringProp?name="variableNames">{{test_group.CsvParaments}}stringProp>
12????CSVDataSet>
13????<hashTree/>
14{%?endif?%}
3.Case選擇用例列表自動篩選壓測用例目錄下的用例選擇對應用例創建壓測計劃
壓測執行1.壓測執行代碼實現
1try:
2????RunningProcess?=?subprocess.Popen(r'{}?-n?-t?"{}"?-j?"{}"'.format(jmeter_abspath,?tmp_jmx_abspath,?tmp_log_abspath))
3????RunningId?=?str(data["project_id"])?+?"-"?+?str(RunningProcess.pid)
4????RunningProcess.wait()
5????server_running.remove(ShowContent)
6except?Exception?as?Error:
7????server_running.remove(ShowContent)
8????os.remove(tmp_jmx_abspath)
9????return?JsonResponse(code="999998",?msg="發生{}錯誤".format(Error))????2.壓測執行前端展示
壓測實時監控壓測完成后從對應服務器的grafana接口獲取服務器信息實現實時監控
1if?hostip:
2????mem_query?=?'query=(1?-?(node_memory_MemAvailable_bytes{instance=~"'?+?hostip?+?':9100"}?/?(node_memory_MemTotal_bytes{instance=~"'?+?hostip?+?':9100"})))?*?100'
3????max_mem,?average_mem?=?getInfoFromGranafa(startTime,?endTime,?mem_query)
4????cpu_query?=?'query=avg(irate(node_cpu_seconds_total{instance=~"'?+?hostip?+?':9100",mode="user"}[2m]))*100'
5????max_cpu,?average_cpu?=?getInfoFromGranafa(startTime,?endTime,?cpu_query)
6????load_query?=?'query=node_load1{instance=~"'?+?hostip?+?':9100"}'
7????max_load,?average_load?=?getInfoFromGranafa(startTime,?endTime,?load_query)
8????tr.mem_max?=?max_mem
9????tr.men_average?=?average_mem
10????tr.cpu_max?=?max_cpu
11????tr.cpu_average?=?average_cpu
12????tr.load_max?=?max_load
13????tr.load_average?=?average_load
14tr.save()????
壓測時實時數據展示
壓測時服務器性能數據實時展示
壓測報告比對結果的實現壓測報告比對代碼
1fillColor:?function(obj)?{
2if?(obj.columnIndex===2){
3????if?(obj.row.cpu_max.indexOf('/')?>=?0)?{
4????????return;
5????}
6????let?strs?=?obj.row.cpu_max.split("?");
7????if?(strs.length?3)?{
8????????return;
9????}
10????let?ratio?=?Math.abs((parseFloat(strs[0])?-?parseFloat(strs[2]))/parseFloat(strs[0]));
11????if?(ratio?0.2)?{
12????????????return;
13????}
14????if?(obj.row.cpu_max.indexOf('↑')!==-1){
15????????return?{color:'red'};
16????}?else?{
17????????return?{color:'blue'};
18????}
19}???后端比對數據實現
1if?int(left_data.get('FiftyLine'))?int(right_data.get('FiftyLine')):
2????merge_data['FiftyLine']?=?left_data.get('FiftyLine')?+?'??'?+?right_data.get('FiftyLine')?+?'?↑'
3else:
4????merge_data['FiftyLine']?=?left_data.get('FiftyLine')?+?'??'?+?right_data.get('FiftyLine')
5
6if?int(left_data.get('NintyLine'))?int(right_data.get('NintyLine')):
7????merge_data['NintyLine']?=?left_data.get('NintyLine')?+?'??'?+?right_data.get('NintyLine')?+?'?↑'
8else:
9????merge_data['NintyLine']?=?left_data.get('NintyLine')?+?'??'?+?right_data.get('NintyLine')
10
11if?int(left_data.get('NintyNineLine'))?int(right_data.get('NintyNineLine')):
12????merge_data['NintyNineLine']?=?left_data.get('NintyNineLine')?+?'??'?+?right_data.get(
13????????'NintyNineLine')?+?'?↑'
14else:
15????merge_data['NintyNineLine']?=?left_data.get('NintyNineLine')?+?'??'?+?right_data.get(
16????????'NintyNineLine')
前端判斷數據是否超過20%閾值修改顏色展示
通過結果的比對,我們可以非常清晰看到本輪技改或者優化配置,性能提升多少還是下降,也能方便的查看到各種資源池的比對。
另外,以每次壓測的結果作為下一次比對性能基線,從而推動性能優化工作持續進行。
本文作者
韓盛,7年測試經驗,擅長性能測試,自動化測試。熟悉java, python。現任職掌門1對1測試開發工程師。
劉萬紅,多年互聯網大廠測試經歷,現任職掌門1對1研發部測試經理 擅長接口/性能/自動化等各種測試平臺開發。
總結
以上是生活随笔為你收集整理的压测接口线程数设置_ZAT掌门性能压测巡检系统实战和落地的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 相关性分析p值_一行代码掌握皮尔逊相关分
- 下一篇: redis最大储存512m_redis系