测试开发之性能测试
前言
在桔厂实习时,对网约车某业务的接口做压力测试,需要开发goperf的接口层压测工具,于是花费几天的时间对压测进行了学习。
在9.30之前,要对一期聚合表单项目,做一个全链路的压测。需要以前做好压测计划预案。
一些基本概念
QPS Queries Per Second 是每秒查询率 ,是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准, 即每秒的响应请求数,也即是最大吞吐能力。简单的说,QPS = req/sec = 请求数/秒。它代表的是服务器的机器的性能最大吞吐能力。
TPS 即 Transactions Per Second的缩写,每秒处理的事务数目。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数,最终利用这些信息作出的评估分。TPS 的过程包括:客户端请求服务端、服务端内部处理、服务端返回客户端。
Qps 基本类似于 Tps,但是不同的是,对于一个页面的一次访问,形成一个 Tps;但一次页面请求,可能产生多次对服务器的请求,服务器对这些请求,就可计入“Qps”之中。
例如,访问一个 Index 页面会请求服务器 3 次,包括一次 html,一次 css,一次 js,那么访问这一个页面就会产生一个“T”,产生三个“Q”。
什么是性能测试?
性能测试是指通过脚本或者专门的工具来尽可能的模拟真实的流量,进行各种正常的、峰值以及异常负载条件对被测模块或者系统的各项性能指标进行测试。 性能测试一般分为负载测试和压力测试。
负载测试:通过各个不同的流量,来确定当前的工作负载下被测系统的性能指标,目标是测试当流量压力逐渐增加时,系统各项性能指标的变化情况。比如:当一秒钟对被测系统发2000条请求/3000条请求/4000条请求的情况下,被测系统的占用的cpu情况,服务端的响应时间,处理成功的请求比例,占用的系统内存,写磁盘的利用率等指标的变化情况。
压力测试:通过负载测试的基础上不断增大发送请求的流量,来确定一个系统的瓶颈点,从而确定系统能够提供的最大性能指标。另外,压力测试不仅仅是给出一个数据,当被测模块出现瓶颈时,通过一定的方法分析性能瓶颈,给出系统需要优化的方向。
为什么要做性能测试?
性能测试是衡量一个系统或者模块能否承受多大的流量。开发工程师,测试工程师以及运维工程师根据性能测试得出的系统的性能指标,结合线上的数据进行线上系统各模块的部署。
在没有进行性能测试的前提下将系统或者模块发布上线,系统很有可能会由于承受不了线上的压力而崩溃,造成不可挽回的损失。
- 压测的目的是在确保系统在当前流量N倍情况下是能够正常、稳定运行的。
- 压测的根本目的就是发现高流量水位下的系统问题,并解决这些不稳定性,同时摸清系统的极限流量水位。
压测要做什么?
业务理解:性能测试不是简单的执行测试工具,最后给出一些数据。每个系统都有自己的特点,性能测试的同学首先需要对被测系统特别的了解,了解具体的业务,提炼出关键场景,评估衡量系统性能的指标。
性能场景设计:了解业务,提炼出关键场景之后,需要对每个关键业务场景设计性能测试用例。
性能测试数据准备:构造符合真实场景的数据,保证性能测试的结果是有参考价值的。
性能环境搭建:搭建被测系统的性能测试环境。
性能工具准备: 性能工具的选取也比较关键,首先需要了解性能工具的实现原理,以及性能工具本身的性能如何,选取的性能工具与被测系统是不是相匹配。一个良好的压力测试工具可以让测试结果更精确。目前咱们公司内部使用比较多的工具是ultron , jmeter等工具。
性能测试目标:建议有明确的性能测试目标,可以以接口为纬度,也可以以场景的纬度来定目标,可以参考下面的指标
测试结果分析与报告:发送性能测试报告的目的是让所有相关人员知晓被测模块或者系统当前的性能指标。最好分析出瓶颈点,让相关的开发人员的后期优化有个方向。 测试结果,以后会自动化生成二部分:基础性能指标,监控数据。问题分析需要QA及相关的RD介入来定位问题,当然下面会介绍如果定位问题。
核心链路梳理
明确压测目标,优先压测核心接口与核心链路
压测不是一个人的事,也不是一个系统的事
明确链路中涉及了哪些上下游接口调用。
明确调用链路涉及了哪些中间件
核心监控梳理完善,监控帮助我们明确压测范围,告警帮助我们明确什么时候停止压测。
最终针对这条核心链路,需要给出你的期望压测指标,即
压测期望到达的 QPS
压测期望平均响应时间
实际项目1:接口性能测试
如本接口主要关注的指数有qps,avg_time,cpu idel
qps是用总请求数/时间秒,sum_qps = float64(sum_count)/time.Now().Sub(start).Seconds()
avg_time开始结束时间做差:sum_avgtime = float64(sum_time)/1000000/float64(sum_count)
cpu_Idel:CPU处于空闲状态时间比例,Linux中的top命令可实时显示系统的CPU使用情况,但实时显示并不利于统计,于是我们用Nmon采集工具进行采集。配合Nmon-Analyser进行分析。
明确需要分析的这三个指标后就好办了。qps和avg_time都可以通过自己内部开发的goperf工具来统计,但是cpu_Idel就没办法统计了。总不能通过top命令一直实时的展示,(如果是线上的机器可以直接连接到odin监控平台进行监控,但是由于我机器是用的线下docker,就只能通过手动统计)由此引出了nmon。
nmon的安装和使用
Nmon:Nmon是一款计算机性能系统监控工具,因为它免费,体积小,安装简单,耗费资源低,广泛应用于AIX和Linux系统。nmon的使用需要2部分:nmon采集数据和nmon_analyzer可视化分析数据
nmon安装包:根据服务器类型选择对应的nmon版本
http://nmon.sourceforge.net/pmwiki.php?n=Site.Download
1
2
3
4
5
6
7
8
9
# 想办法下载如wget ftp等方式
# 解压下载的nmon压缩包
tar zxvf nmon16m_helpsystems.tar.gz
#为了使Nmon在当前操作系统环境中任何路径下都能运行,在64位CentOS中,需要将nmon_x86_64_centos7文件移动到执行文件夹bin目录(环境变量目录)之下,并命名为nmon
mv nmon_x86_64_centos7 /usr/local/bin/nmon
# 为nmon配置[可读、可写、可执行](-rwxrwxrwx)权限(64-bit系统)
chmod +x nmon_x86_64_centos7
# 采集数据
[root@fc3a8aece645 bin]# ./nmon -s10 -c30 -f -m /home/xiaoju/report
参数解释:
-s10 每 10 秒采集一次数据。
-c60 采集 60 次,即为采集十分钟的数据。
-f 生成的数据文件名中包含文件创建的时间。 -m 生成的数据文件的存放目录。
这样就会生成一个 nmon 文件,并每十秒更新一次,直到十分钟后。
生成的文件名如: _090824_1306.nmon ,”” 是这台主机的主机名。
4.性能指标查看
主要查看指标包括:CPU使用情况、磁盘I/O、内存使用情况、网络使用情况等
nmon_analyser工具
下载地址:https://developer.ibm.com/technologies/systems/articles/au-nmon_analyser/
1.解压需要的文件nmon analyser v66.xlsm
2.调整excel宏的安全级别,调整为最低或者如下操作

绘图如下所示:

实际项目2:全链路压测
9.1补充,最近半个月,独立负责了一期聚合表单的项目,在930之前需要做一个大型的链路压测。
背景:随着中秋以及十一到来,必将迎来一波新的高峰,全链路压测当前主保核心链路,仍然有部分核心服务、核心场景或者可以影响到核心链路的模块容量评估不到位,需要系统化梳理当前各子链路方向存在的容量问题,提前排查稳定性风险。
在这里分事前事中事后三个角度。
压测前
明确压测链路,梳理核心链路/场景,负责人
明确压测目标:预估,发单,完单,完成率,司机在线数等场景。原则:业务方提供日完单数,根据28原则(80%流量在一天当中20%时间内完成)推算出关键接口qps
压测数据构造:线上引流
发压工具:arcana、ultron改造
压测环境:物理机房,环境是否需要隔离?
存储层数据隔离:日志单独写入,压测数据写入。
依赖接口如何处理:mock
接口的分级,降级,限流方法。
风险排查:排查各方向全链路压测未覆盖或者覆盖度不足的链路。
- 核心链路&影响核心链路(哪些链路全链路压测流量没有覆盖到或者全链路压测流量覆盖不足)
- 自身容量(核心模块容量风险梳理)
- 链路容量与验收(链路容量风险梳理以及子链路压测验证)
- 机器资源(是否有buffer)
- 预案有效性
- 限流合理性
压测中
压测阶段观察监控
- 线程池情况
- 服务cpu情况
- 服务内存使用率
- 接口响应时长,错误率
- 接口qps
- jvm情况
- 网络io情况
- 服务错误日志数
- Rediis/mysql等存储层的ops,cpu,耗时情况,连接数等,
记录问题及时止损
压测后
针对问题进行改进
压测挑战
系统首次压测的不确定性,
如何在有限的时间内确保压测的顺利完成
上游依赖稳定性如何,是否和工单的压测目标冲突
系统链路梳理的颗粒度
压测安全可控性
如何确保业务流量和线上流量不错乱
如何避免因为工单压测对上下游服务带来的影响
如果线上发生故障,如何最快止损不影响业务
压测结果可参考性
一次成功的压测必然要保证压测结果的准确以及可参考,如何确定压测环境,如何构造压测数据。
压测框架,奥创平台。压测方案的重点是保证业务流量,业务数据与压测流量,压测数据的隔离。
4.1 流量隔离
压测流量主要分为http流量、dubbo流量、mq流量三种,实现原理都是通过将压测标识依次向下游链路传递,来达到逻辑上的隔离。正因是逻辑隔离,所以有可能导致业务流量与压测流量错乱,进而对业务带来致命灾难(实际上其他团队线上压测也的确出现过此问题)。
4.2 数据隔离
压测数据主要分为日志文件、mysql数据、mongo数据、redis数据,四者都实现了物理隔离:
- 日志文件通过将压测流量写入压测目录实现数据隔离;
- mysql通过为线上表建立影子表实现数据隔离;
- mongo通过为线上表建立影子表实现数据隔离;
- redis通过对缓存key加压测前缀实现数据隔离;
5.压测执行
5.1 压测过程管理
工单压测涉及资产、设备、标签、九霄、QA、中间件等多个团队,为了保证压测计划顺利进行,事先一定要对目标、方案、资源、计划、结果做好协调。跨团队合作是最大的不确定性,为应对过程中存在的风险,我们做的最多的就是组各种会拉齐认知,责任到人,所有结果均落实wiki,积极跟进每个团队的进度,方法看似普通,但很有效。
5.2 压测目标设定
压测目标分为压测内容设定和压测目标值设定两部分。压测内容我们选择了工单平台最重要、流量最大的两条链路:创建和派发。压测目标值参考了今年冲单期:4.30日当天的峰值为我们的基础值,在基础值之上,将目标值设定到了基础值的2倍,如表1所示:
| 方法 | 统计日期 | 峰值(QPS/TPS) | 目标值(QPS/TPS) |
|---|---|---|---|
| WORK_ORDER_TRIGGER_DELAY_EVENT_TOPIC(电单车Delay事件消息) | 2020.04.30 | 2600 | 5200 |
| workOrderCreateRemoteService#createWorkOrder(创建工单) | 2020.04.30 | 200 | 400 |
表1 业务值与目标值
关于为什么要设定2倍,这里说明下:截止到4月底两轮车单车投车570万、电单车投车210万。预计9月份冲单期单车投车将达到650万、电单车达到300万。所以从车辆维度看将目标值设定为2倍远远高于业务值,符合预期,同时可以对工单系统进行容量的摸高,另外也是根据自己以往的大促备战经验(通常以最低2倍流量进行备战和压测)决定的。
5.3 上游流量评估
为了避免工单压测导致上游系统流量的无限放大,我们以工单创建1TPS为基础,针对链路上每个上游依赖统计出了到方法粒度的调用量,以此供上游团队了解自身系统流量峰值及做好自身的降级预案,避免出现故障,如表2所示:
出现故障,如表2所示:
| 依赖上游 | 服务 | 方法 | 调用量 |
|---|---|---|---|
| assets (资产) | AssetQueryService | queryAssetInfo | 1QPS |
| assets (资产) | BhVehicleBgQueryRemoteService | getVehicleInfoByVehicleIds | 1QPS |
| vehiclemgr(设备) | VehicleBgService | getVehicleListByVehicleIds | 1QPS |
| vehiclemgr(设备) | getVehicleBaseInfoByVehicleId | 1QPS | |
| vehicle.profile (标签) | VehiclePosRemoteService | queryLatestVehiclePos | 2QPS |
| vehicle.profile (标签) | vehicleTagRemoteService | addVehicleTag | 1QPS |
| vehicle.profile (标签) | AssetEventSnapshotService | queryAssetEventSnapshotInfo | 1QPS |
| nsky (九霄) | StrategyClient | query | 1QPS |
表2 上游方法流量
5.4 压测集群选取
工单平台只有一个线上集群,我们并没有单独出一个压测集群,而是直接在线上集群进行压测。这样的好处是压测的结果直接反应了系统真正能抗住的量,但同时也存在较大的风险:压测可能会直接导致线上集群出现问题,进而影响业务。所以这就要求我们有完善的降级预案,下文会提到。
另外一种压测方案是对集群内某一单机器进行压测,压到瓶颈后得出的压测值换算成集群可以抗住的量:单机器压测值 * 集群机器数,这种方案可以避免上述问题:影响线上业务,但缺点是由于每台机器的配置不同或者即使每台机器配置相同,但受每台机器所在物理机的影响,压测出的目标值相对也会不准确。
5.5 压测数据构造
压测数据分为基础数据和流量数据。基础数据即依赖的上游车辆数据、人员数据、车辆标签数据、策略数据等,此类数据构造的通常做法是,直接在线上库建立影子表,通过DBA或自行脚本方式直接将线上数据经过简单清洗灌入到影子表。流量数据即模仿用户的请求,为了更贴近工单的线上环境,我们统计出了工单入口流量和最终落mysql库流量的比值,以此来构造流量数据,如图5所示:
线上流量:工单内部逻辑过滤后消息量(2600qps)-创建工单-工单生成(110tps)。
模拟流量比:
5.6 压测时间确定
工单平台的高峰期主要集中在晚上21:00 – 凌晨02:00,所以我们的压测时间选在了低峰期:中午12:00 – 14:00。
5.7 降级预案制定
预案制定我们分为了事前、事中、事后三个阶段:
- 事前:多轮小流量压测,确保压测数据都能正常写入影子表/影子目录;
- 事中:发现问题及时停止加压,且关闭压测入口流量开关,丢弃压测流量;
- 事后:对错误数据进行脚本恢复及压测复盘;
6.总结
当前工单平台已经具备常态化压测的条件,随时可以根据需求以极少人力快速的进行一次针对性的压测,同时为未来工单平台的预案演练、放火演练打下了基础。
但我们的压测还有很多不足,比如压测流量的构造,目前是人工构造的方式,虽然是根据线上历史数据计算出不同类型业务的比例去同比例还原,但还不能完全反应出线上业务请求的特征,希望未来可以接入流量回放等工具,使压测结果更具参考性。最后,希望这篇文章对没有经历过压测的读者可以带去一些收获。