写在开始
在经历了一段相对较长的时间后,在最近我完成了性能测试平台初版的所有功能,这是我在跨越近一年时间中的最大收获。最初有看到网上有个人或团队开发的性能测试平台,就在想如果要实现这么一个工具,这个工具应该是什么样子,应该怎样去做。那时我个人写的接口自动化测试平台已经在公司小范围的使用,同时在不断的优化功能,便暂时将其保留为一个想法。在去年打算将这个想法变为现实,便一点一点的摸索与思考,一点一点的将其实现。由于只是利用业余时间,并且这个过程遇到不少困难,这个系统的实现跨越了较长的时间。相比较之前实现的接口自动化测试平台,这次在做性能测试平台时一个比较大的进步是在开始写代码前,设计阶段准备的充分很多,这次做了完整的原型,不再是一边写一边设计新页面。
开源工具在功能和专业性上已经足够好了,为什么还要做平台化,平台化的意义是什么呢?我认为工具平台化的好处有以下几点:1.方便团队协作和数据整合:开源工具大多是C/S架构的,不利于团队之间共享和交互,也无法归集和整合历史数据;2.定制化功能:平台化在开源工具的基础上可以结合业务实现自定义功能,与外部系统对接,使得测试变得更加灵活和方便;3.降低成本:界面化比原有系统会操作相对简单,降低了压测环境的维护成本、操作成本和管理成本;4.个人提升:提升个人在代码、性能测试方面的深度。
目前有非常多的开源性能测试工具,它们的功能都非常完备和成熟,所以在最开始,设计的理念就是在压测功能上尽可能的复用成熟工具的功能。jmeter作为一个开源的性能测试工具,在业内具有权威性的地位,所以这个性能测试平台基于jmeter进行开发。技术栈使用vue+springboot前后端分离,基础的业务框架使用开源的Lin-CMS管理系统,其提供用户、权限、认证等基础功能。数据库使用mysql5.7、mongodb4.2、influxdb1.8,文件存储使用minio文件服务器。实现了用例与测试数据管理、分布式压力测试、实时压测数据查看、测试结果查看与下载、历史测试数据查询和测试结果分析等功能。
源码地址:https://github.***/guojiaxing1995/easy-jmeter
原型地址:https://modao.***/app/Qf56LAncrokbxs3iOBMRap
EasyJmeter性能测试平台
1 系统架构
用户访问web页面,再由页面通过http请求访问业务后端服务。每台压力机服务器(只支持linux)上有且仅有一个jmeter,同时安装一个agent来控制jmeter运行,agent和服务端使用socketio实时通讯,用于服务端下发指令和agent上报状态。业务数据使用mysql进行存储,测试结果的详细数据使用mongodb存储,压测过程中的热数据使用influxdb存储,测试用例脚本、压测数据文件和压测结果文件(日志、jtl、报告)使用minio文件服务器存储。
2 表结构设计
mysql中除去原本CMS框架系统自带的表,测试平台业务表有6张
表名 | 描述 |
---|---|
project | 工程表,管理测试用例的单位 |
machine | 压力机表,管理压力机 |
file | 文件表 |
case | 用例表 |
task | 记录表,记录一次压测执行 |
task_log | 记录日志表 |
mongodb中业务集合有2个
集合名 | 描述 |
---|---|
report | jmeter报告数据 |
statistics | 单个用例的历史整合数据 |
3 测试平台生命周期
平台化对压测进行管理,就要定义一次性能测试的各个阶段,这些阶段组成了压测平台的生命周期。生命周期包含了四个阶段,分别是配置、运行、收集和清理。配置阶段主要为将测试所需的数据如jmx脚本、csv文件、扩展jar文件从minio下载到各个压力机,将需要切分的csv文件进行切分,切分就是将一个csv文件内的数据按照压力机的个数分为多份,最后配置脚本和配置文件等参数。配置成功后agent向服务端报告状态,待所有压力机配置完成服务端下发运行指令,进入压测状态,压测状态为jmeter持续运行中并收集测试数据。所有压力机运行完成上报后统一进入收集状态,上传测试结果文件并对结果进行处理并归集。所有压力机都完成收集后进入清理状态,每台压力机重置测试环境。在这一系列阶段中,平台不能丢失对各个阶段的管理,系统在当前阶段出现问题后要可以闭环,所以前三个阶段失败后都会进入清理阶段。同时可以手动的停止前三个阶段,终止压力测试。
4 分布式压测
jmeter本身是支持分布式压测的,但存在一些缺陷,如需要不少额外配置,master节点不参与压测浪费资源,测试数据实时回传master浪费带宽。压测平台本身就已经实现通过agent控制jmeter,agent和服务端通过长连接进行实时通信,在此基础上,由服务端控制各个压力机运行,平台就实现了对于压力机的分布式调度。
5 压力机管理
一台压力机上部署一个agent服务和jmeter,jmeter需要将其安装路径设置为环境变量JMETER_HOME,agent根据环境变量定位jmeter,每分钟判断一次jmeter是否可用并上报ip、版本、路径给服务端,和服务端维护的ip地址匹配作为压力机是否在线的依据,匹配一致的压力机会更新jmeter的版本和安装路径。
6 用例管理
用例管理可以新增、编辑、查询、调试、删除用例,可以对目标用例进行启动测试、终止测试、查看测试详情、动态控制TPS和跳转用例的历史记录列表。
6.1 新增、编辑用例
用例新增、编辑时填写用例名称、选择项目,项目为当前系统维护,用来进行用例分类。
上传保存好的jmx文件、csv文件、jar文件,csv文件可选是否切分。
6.2 调试用例
在列表页面点击调试按钮,可以打开调试弹框,点击启动对当前用例以一个线程发起一次请求.如查看结果树一样,可以查看接口的响应和请求信息。
点击引擎日志,可以查看对应请求的jmeter日志
6.3 启动测试
在列表点击启动按钮弹出测试启动弹框,选择运行的并发线程数、压测时间、启动线程的时间、容量控制,容量控制会在下文详细展开。压力机可以选择个数或指定某几台压力机。日志等级为jmeter日志等级。报告时间颗粒度为jmeter生成报告图表的时间采样频率,默认0为系统内置采样率,为0时系统会根据压测时间来设置采样间隔。实时数据展示开关控制本次测试是否记录实时数据并展示。
在测试过程中可以通过列表或详情的进度条查看当前的状态,运行状态的测试可以查看已运行时间的百分比。
6.4 动态控量
这里的动态控量是指吞吐量的限制与动态调节。这个高阶知识点最早是在其他文章中看到的。
在压测时,“控量”是非常重要的,JMeter 是根据线程数大小来控制压力强弱的,但我们制定的压测目标中的指标往往是吞吐量(QPS/TPS),这就给测试人员带来了不便之处,必须一边调整线程数,一边观察QPS/TPS 达到什么量级了。为了解决这个问题,JMeter 提供了吞吐量控制器的插件,我们可以通过设定吞吐量上限来限制 QPS/TPS,达到控量的效果。
上面的做法能够确保将吞吐量控制在一个固定值上,但这样还远远不够,实际工作中我们希望在每次压测执行时能够随时调节吞吐量,比如,在某个压力下服务容量没有问题,我们希望在不停止压测的情况下,再加一些压力。方案就是将目标吞吐量设置为变量,在测试过程中调整这个值。
修改jmeter.properties参数
#---------------------------------------------------------------------------
# BeanShell configuration
#---------------------------------------------------------------------------
# BeanShell Server properties
#
# Define the port number as non-zero to start the http server on that port
beanshell.server.port=9000
# The tel*** server will be started on the next port
# Define the server initialisation file
beanshell.server.file=../extras/startup.bsh
执行测试过程中,通过外部命令访问BeanShell服务就能植入我们的BeanShell脚本和全局变量
java -jar <jmeter_path>/lib/bshclient.jar localhost 9000 update.bsh <参数>
自定义的吞吐量更新脚本update.bsh如下:
import org.apache.jmeter.util.JMeterUtils;
getprop(p){ // get a JMeter property
return JMeterUtils.getPropDefault(p,"");
}
setprop(p,v){ // set a JMeter property
print("Setting property '"+p+"' to '"+v+"'.");
JMeterUtils.getJMeterProperties().setProperty(p, v);
}
setprop("throughput", args[0]);
在平台中可以通过页面修改吞吐量限制
6.5 测试详情
点击用例进入目标用例最近一次测试的详情页面,进行中的用例会展示测试进度。顶部显示用例的基础信息,下方显示测试情况,有并发线程数、压测时长、测试人员、压测开始结束时间、压力机台数,可以下载jmeter日志、测试脚本文件。测试完成后会显示聚合报告。
6.6 环节日志
测试时实时展示测试阶段每一个环节的进度以及对应环节下每一台压力机的环节结果。更好的管理生产周期状态和排查问题。
6.7 实时数据
jmeter支持 Backend Listener 将测试结果实时发往 InfluxDB,平台向 InfluxDB 轮询查询数据,并展示在页面上。目前展示了总TPS、总错误趋势、错误事务统计,以及指定事务筛选,查看对应事务TPS、错误趋势和错误类型分类。除此以外,也可以集成开源数据可视化系统Grafana,进行更加丰富的数据展示。
6.8 测试结果
在测试完成后,可以查看测试图表信息,包含TPS、平均响应时间、总TPS、随时间变化的响应时间百分位数(成功响应)、活动线程数共5个图表,以及错误类型占比和错误排行榜top5。点击右上角报告下载按钮,下载jmeter测试报告可查看更多详细数据。
7 测试记录
测试记录模块可以查看所有的测试记录,支持按照测试编号、用例名称、测试时间、测试结果进行查询。点击详情跳转对应记录的测试详情页,
7 用例分析
顶部可查看系统数据,包含项目数、用例数、压力机数、测试次数、压测总时长以及总请求数。
左侧为用例列表,支持查看指定用例的平均响应时间、90th响应时间、平均吞吐量、测试次数、压测时长和请求数。支持查看多次测试的响应时间趋势、吞吐量和错误率,可以帮助测试与开发人员在系统优化后,一目了然的查看优化情况。
8 系统部署
8.1普通部署
- 安装mysql5.7数据库
- 安装mongodb4.2数据库
- 安装influxdb1.8数据库
- 安装minio文件服务器,设置指定bucket的A***ess Policy为public
- 部署server、agent。代码结构中api目录下为后端目录,后端使用springboot,修改配置文件并打包。其中作为server启动时设置socket.server.enable为true,作为agent启动时设置socket.client.enable为true,agent需要设置服务端socketio地址serverUrl。agent所在压力机需要配置jmete安装路径作为环境变量JMETER_HOME.
- 前端服务打包。代码结构中web目录下为前端服务,前端使用vue,node版本v12.13.0,打包命令 npm run build。
- 安装nginx。将web目录下default.conf按照实际情况修改,将前端包和配置文件放入nginx指定目录下启动。
8.2容器化部署
- 构建后端jar包,代码结构中api目录下为后端目录,后端框架springboot,maven构建命令,
mvn clean package
。 - 构建前端dist包,前端使用vue,node版本v12.13.0,打包命令
npm run build
。 - 编辑项目根目录下docker-***pose.yaml 文件,修改environment中的minio地址和influxdb地址为实际地址,修改volumes中宿主机jmeter地址为实际地址。项目根目录下执行
docker-***pose up -d
构建镜像并启动。
写在最后
对于测试工具或其他效能工具,我认为它最重要的就是要好用,这一点涉及到页面交互、功能设计等放放面面。之前做的接口自动化测试平台,有的同事认为可以方便的实现接口的测试,但也有同事认为在使用上还不够友好,我也持续进行了优化,并增加了一些功能,最终还算满意。但后期不管怎样去改,它的基本功能结构在一开始就已经定了,所以设计阶段的工作尤其重要,这次在设计上投入了不少时间。但很多事情并不能等完全想明白再去做,因为有时候事情存在难度、在遇到困难时是没有办法完全理清的,遇到这种时候,就在尽可能准备充分后下手去做,迈开第一步。测试平台已经具备了基本的性能测试功能。目前只进行了初步的使用,在后续结合实际业务使用中,我会不断进行功能优化。如果有好的想法和建议,欢迎随时沟通。