当前位置: 首页 > 编程日记 > 正文

压测接口线程数设置_ZAT掌门性能压测巡检系统实战和落地

项目背景

随着业务拓展,对于接口性能的要求也在上升,各部门也开始针对部分慢接口进行优化,从测试角度针对这些优化需求进行测试时不仅要保证对应接口的功能正常使用同时也要验证接口优化成果。在日常的开发工作中一些后台服务配置的改动也会对接口的性能产生影响,我们急需要一套性能压测巡检平台,对平时接口服务的技改,后端配置的优化进行持续的性能验证。

项目日常痛点

1.测试与开发都无法确定这次改动优化程度如何,与之前比较究竟提升多少性能。只能验证相关功能正常。

2.测试不清楚开发优化接口用了那些方法需要观察那些指标。

3.优化接口的改动与新增接口是否存在性能bug无法确定。

为了清楚的展示每次优化的成果,对新上线的接口检查是否存在性能bug。基于目前的zat自动化平台开发了性能压测功能。

ZAT性能压测巡检实现方法

1.ZAT性能巡检平台实现机制介绍

①平台后台使用jmeter执行自动化case本身就支持对于后端接口的性能压测,通过对平台的改造可以快速实现在平台上对于接口性能压测的功能。而不需要重新开发一套工具。

②性能压测使用的case可以直接复制自动化case,修改几个变量的配置可以直接用于性能压测。节省编写压测用例的时间,同时使用自动化用例还可以模拟用户使用场景。

③压测结果通过平台读取直接存入数据库,可以对多次性能压测数据比对,接口优化时也可以通过历史数据来比对是否有优化。

2.后期还需要解决的问题

Jmeter运行压测时占用系统资源多,同时目前针对uat环境进行压测大部分服务只有1台机器,为了放置多部门压测时对公共服务的调用影响到接口性能的数据。所以现在设置成同一时间只允许运行1个压测job。其他端可以看到当前那个组在压测状态和进度。

之后会采取2种修改改进这个问题:

①采取分布式压测,各组可以自己提供压测机器通过slave的方式分担压测压力。②等服务端接入docker后,通过各种策略将请求发送到对应容器。避免多个部门压测同时调用同一个公共服务导致影响到性能数据ZAT性能压测巡检平台实现架构图

fd73a55f494937496de61a527fb92684.png

ZAT性能压测巡检平台流程图

e17c71d2065cc5a74141c2dfc8f87c77.png

目前zat自动化平台后端使用jmeter来执行case。基于原有功能在之前的基础上加入jmeter自身线程组与压测所需的csv数据文件的设置进行改造,实现了压测用例的生成,执行。通过对jtl文件中各性能数据的计算获取接口相应时间,吞吐量,95线,99线等数据,同时调用grafana接口获取压测时对应服务器的内存,cpu和负载数据。汇总收集到的数据存入数据库。用来跟历史数据或者之后的数据进行比对。

ZAT性能压测巡检平台的实现ZAT性能压测巡检平台执行压测的流程如下

f59a65c0758a764188032a6894cf6cd4.png

API信息设置

压测中需要关注对应的服务器的内存 CPU所以要在对应压测case所使用的API信息中增加对应的APPID与需要压测的IP地址

4468a4526090a9849b592860500381bb.png

压测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.压测执行前端展示

35ffd79a3c30c7a8e053f2a1cce84f38.png

压测实时监控

压测完成后从对应服务器的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()    

压测时实时数据展示

598df636728445e0a4d3cd93a22dc9e1.png

压测时服务器性能数据实时展示

7ae7997f239a49f1ad8df231db64ffbd.png

压测报告比对结果的实现

压测报告比对代码

 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%阈值修改颜色展示

6860fc5d88bdfefdcf470e8ae2d725ca.png

通过结果的比对,我们可以非常清晰看到本轮技改或者优化配置,性能提升多少还是下降,也能方便的查看到各种资源池的比对。

另外,以每次压测的结果作为下一次比对性能基线,从而推动性能优化工作持续进行。

462ffe3f09b3385fc497271db89a0e8d.png

本文作者

韩盛,7年测试经验,擅长性能测试,自动化测试。熟悉java, python。现任职掌门1对1测试开发工程师。

刘万红,多年互联网大厂测试经历,现任职掌门1对1研发部测试经理 擅长接口/性能/自动化等各种测试平台开发。

相关文章:

01python语言程序设计基础——初识python

1.python的字符串中format函数用法 format 函数可以接受不限个参数&#xff0c;位置可以不按顺序。In [2]:"{} {}".format("hello", "world") # 不设置指定位置&#xff0c;按默认顺序Out[2]:hello world In [3]:"{0} {1}".format(&q…

没有什么不可能(1)

近在读一本书《没有什么不可能》&#xff0c;书中宗旨就是&#xff1a;这个世界没有什么不可能&#xff0c;每个人的脚下都有一条通往成功的道路&#xff0c;信念是一切力量的源泉。这本书看了三分之一&#xff0c;跟大家分享一下前三个观点。 1、只有想不到&#xff0c;没有做…

浅浅认识之VBS脚本访问接口与COMODO拦截COM接口

这2天测试了一个使用了WMI提供ASEC后门&#xff0c;里面使用了JS脚本往外请求http获取执行命令。但我的分析系统却没抓到这个行为&#xff0c;可在真机中确实抓到有HTTP请求。相当奇怪。 最后无奈windbg出手&#xff0c;内核断点afd 发送函数。最后发现是scrcons.exe进程&#…

redis最大储存512m_redis系列篇01

今天写的这篇是redis系列的文章&#xff0c;我的安排是由浅入深写redis系列。本篇是简单的介绍入门&#xff0c;后续的文章会详细讲解redis深层次的知识。欢迎大家关注我的微信公众号&#xff1a;码农Bug首先说几个简单的命令&#xff1a;keys *:查询所有的键值del key&#xf…

批处理命令——goto 和 :

谈起goto&#xff0c;相信大家应该想到的是面向过程编程。其实&#xff0c;这就相当于当有人向你谈起class&#xff0c;意味着你就懂得面向对象编程。如果你不懂&#xff0c;那么你们的沟通将会很困难。不懂我说的啥意思吗&#xff1f;请参见曾经分享王路的一篇文章《永远不要对…

浮动布局会受父框滚动条影响

此时的效果是&#xff1a;如果此时把父框的滚动条去掉或隐藏掉&#xff1a;而此时的效果是:总结:1 有时我们的布局发生了改变可能就是受到出现滚动条的影响了 而我们很容易忽略掉这一点转载于:https://blog.51cto.com/11871779/2387118

Delphi7的主窗口

Delphi7的主窗口转载于:https://www.cnblogs.com/LoveFishC/archive/2012/08/10/3845692.html

线程组多次调用_详细分析 Java 中启动线程的正确和错误方式

start 方法和 run 方法的比较代码演示:/** * * start() 和 run() 的比较 * * * author 踏雪彡寻梅 * version 1.0 * date 2020/9/20 - 16:15 * since JDK1.8 */public class StartAndRunMethod {public static void main(String[] args) {// run 方法演示// 输出: name: main//…

Concurrency Runtime in Visual C++ 2010

PDC 2010 Hejlsberg的演讲中我们看到了VB.NET、C#新的简化异步编程的方式&#xff08;可以下载新的Async CTP体验&#xff09;。之前的TPL&#xff08;Task Parallel Library&#xff09;简化了并行编程。工业语言的飞速发展大大改进、简化了开发人员的编程方式。不仅是微软平台…

关于安卓你不知道的6件事

安卓第一次亮相是出如今2008年公布的HTC Dream手机上&#xff0c;到如今为止它已经6岁了。或许没有人想过在2010年底它就成为了智能手机平台率先的操作系统。这当然要感谢谷歌的努力和强大的財力支持。尽管眼下安卓系统是世界上最流行的移动操作系统&#xff0c;可是关于它的非…

vmware的三种网络模式讲解

vmware有三种网络设置模式&#xff0c;分别是Bridged(桥接),NAT(网络地址转换)&#xff0c;Host-only(私有网络共享主机) 1.Bridged(桥接) 桥接模式默认使用的是&#xff1a;VMnet0 什么是桥接模式&#xff1f;桥接模式就是把主机网卡和虚拟机虚拟的网卡利用虚拟网桥进行通信。…

当前路径_[JSP] 07 JSP 路径问题

首先先明确一下下列URL的假设一个URL是这样的losthost:8080/myservlet/path服务器根路径:losthost:8080/项目根路径:losthost:8080/myservlet/明确了服务器根路径和项目根路径之后,我们就可以开始学习后面的知识了servlet路径问题请求转发和重定向的相对路径写法总结:Servlet重…

CPU时间戳获取

inline long long timt(){long long p; int&a*(((int*)&p)1);__asm__ __volatile__("rdtsc":"a"(p),"d"(a));return p; } 因为在64位CPU上rdtsc出来的结果仍然是在%eax和%edx,而%rax却不是由%eax和%edx拼起来的(反正我试了它没用...也许…

NYOJ 366 D的小L

地址&#xff1a;http://acm.nyist.net/JudgeOnline/problem.php?pid366 方法&#xff1b;用next_permutation&#xff08;pɝmjʊteʃə&#xff09;来求解&#xff0c;递归调用代码 1 #include<stdio.h>2 #include<algorithm>3 using namespace std;4 int main(…

MySQL 语句整理 2019-5-3

MySQL 语句整理 在整理完Oracle的一些常见用语句后,由于MySQL的语法跟Oracle略有不同,随跟PN的MySQL视频进行了间接整理. 查询薪水大于1800, 并且部门编号为20或30的员工sql select deptno,ename,sal from emp where sal > 1800 and (deptno 20 or deptno 30); and 优先级…

联想e580没有声音_现在你可以购买通过 Linux 认证的联想 ThinkPad 和 ThinkStation

曾经有一段时间&#xff0c;ThinkPad 是 Linux 用户的首选系统。但那是在 ThinkPad 还是 IBM 的产品的时候。来源&#xff1a;https://linux.cn/article-12283-1.html作者&#xff1a;Abhishek Prakash译者&#xff1a;Xingyu.Wang曾经有一段时间&#xff0c; ThinkPad 是 Linu…

linux平台下防火墙iptables原理(转)

原文地址&#xff1a;http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646466.html iptables简介 netfilter/iptables&#xff08;简称为iptables&#xff09;组成Linux平台下的包过滤防火墙&#xff0c;与大多数的Linux软件一样&#xff0c;这个包过滤防火墙是免费的&a…

SQL语法大全

SQL语句大全 --语 句 功 能--数据操作SELECT --从数据库表中检索数据行和列INSERT --向数据库表添加新数据行DELETE --从数据库表中删除数据行UPDATE --更新数据库表中的数据--数据定义CREATE TABLE --创建一个数据库表DROP TABLE --从数据库中删除表ALTER TABLE --修改数据库表…

Node.js的url模块简介

什么是URL URL是Uniform Location Resource的缩写&#xff0c;翻译为“统一资源定位符”&#xff0c;也就是描述资源位置的固定表示方法。被URL描述的资源可以位于互联网上&#xff0c;也可以位于本地。 URL的组成结构 基本URL包含模式&#xff08;或者成为协议&#xff09;&am…

python游戏结束显示分数代码_当游戏循环在Python中运行时,多线程来显示游戏分数?...

我想根据比赛时间的推移得分。为此&#xff0c;我想让两个循环同时运行。游戏循环和得分循环&#xff0c;每1.5秒加1。当我运行程序时&#xff0c;分数不会出现。我是否正确使用多线程&#xff1f;这是最好的办法吗&#xff1f;为了简单起见&#xff0c;我只发布了相关代码。谢…

NOIP2015解题报告 By ljt12138

Day1t1 幻方 练过的一道题&#xff0c;简单模拟&#xff0c;用二维数组存储&#xff0c;ij两个游标记录横纵坐标&#xff0c;利用题目条件改变坐标直到填入n个数即可。复杂度O(n^2) AC Day2t2 图的最小环 首先抽象出图论模型。每个人对应点&#xff0c;传输对应边。因为自己…

设计模式笔记——Adapter

适配器模式Adapter 将一个类的接口转换成用户希望的另外一个接口。适配器模式主要应用于希望复用一些现存的类&#xff0c;但是接口又与复用的环境要求不一样的情况。 package adapter.pattern; /*** * author Real H LI**/ public class Existed {public void OldRequest(){Sy…

iOS12-crash错误-reason: 'UITableView failed to obtain a cell from its dataSource'

错误原因&#xff1a; cellForRowAtIndexPath函数返回了nil override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let cell tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: in…

with as python_python - with as的用法

摘自&#xff1a;http://www.cnblogs.com/DswCnblog/p/6126588.htmlWith 是什么&#xff1f;有一些任务可能事先需要设置&#xff0c;事后做清理工作。对于这种场景&#xff0c;Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理&#xff0c;你需要获取…

wcf系列学习5天速成——第四天 wcf之分布式架构(转载)

今天是wcf系列的第四天&#xff0c;也该出手压轴戏了。嗯&#xff0c;现在的大型架构&#xff0c;都是神马的&#xff0c; nginx鸡群&#xff0c;iis鸡群&#xff0c;wcf鸡群&#xff0c;DB鸡群&#xff0c;由一个人作战变成了群殴....... 今天我就分享下wcf鸡群&#xff0c;高…

检测jQuery.js是否已加载的判断代码

转载自http://www.jb51.net/article/27185.htm 测类、方法、变量或属性是否已存在&#xff0c;这是Javascript编程基础知识。在这里我们就是要检测jQuery()或$()函数是否存在 当然&#xff0c;该方法不局限于jQuery的检测&#xff0c;对与任何Javascript变量或函数都是通用的。…

Java中常见的锁简述

在Java的应用中&#xff0c;或多或少的都会接触到一些锁&#xff0c;那么问题就来了&#xff0c;在Java中&#xff0c;常见的锁有哪些&#xff0c;都有什么样的作用&#xff1f;&#xff1f; 这里给大家简单的简述一下这些学常见的锁。 本文件所涉及到的锁&#xff1a; 1.公平锁…

加了好友怎么还掉血_微信聊天窗口出现风险提醒,无法添加好友解决办法

再更&#xff0c;你想马上解封&#xff0c;你得有朋友。没有朋友&#xff0c;你就等个十天半个月&#xff01;更新一下 帖子浏览人挺多的&#xff0c;有问题可以在评论里相互交流&#xff0c;下面是原文&#xff1a;先让我说一句脏话&#xff1a;傻x微信风控系统 终于特么的聊天…

[小明爬坑系列]AssetBundle原理介绍

一.简介 Assetbundle 是Unity Pro提供提供的功能&#xff0c;它可以把多个游戏对象或者资源二进制文件封装到Assetbundle中&#xff0c;提供了封装与解包的方法使用起来很便利。 二.移动平台特点 Assetbundle是可以把预制,文件以及场景都打包到Assetbundle中去的,但是在移动平台…

Ubuntu16.04菜单栏侧边栏不显示

2019独角兽企业重金招聘Python工程师标准>>> &#xff08;&#xff11;&#xff09;只有桌面上显示一些原有的文档或图片。但打开文档或者图片后也是没有窗口菜单栏&#xff0c;这样连关闭都没法点&#xff0c;快捷&#xff21;ltF4也没有反应。而且鼠标点击拖动不了…