目录
一、性能测试背景:
有效的性能测试能给研发运维团队提供有效的容量规划能力,系统风险识别,性能瓶颈识别,性能调优指导,保障尽量避免如上问题的发生(新系统上线,重构,性能调优)
二、常规的被测系统架构模型
三、性能测试流程
一. 性能需求指标:
- 时间指标:响应耗时,具体根据实际场景的测试需求定义
- 容量指标:同时承载多少人并发/在线,最终落库的订单量和数据量是什么样的规模
- 资源利用率指标:如cpu的安全范围,当达到指标阈值,要做记录,写入报告
二. 性能模型:
- 业务模型:用户行为的动作流程,比如拉新的用户注册,登录等
- 监控模型:
三. 性能方案:
- 测试环境:线下或者线上
- 测试数据:测试数据的构造,和测试模型相互关联,基于测试模型来造测试数据
- 测试模型:和测试数据相互关联,基于业务场景
- 性能指标:从性能需求指标拆解出来
- 压力策略:比如多少并发,tps,qbs达到多少,持续多久等
- 准入准出:跟随性能标准,做什么样的标准才能满足准入条件
- 进度风险:略
四. 性能监控:
这块会结合prometheus具体的讲
五. 性能场景执行:
比如时长,压测范围,目标值
六. 性能结果/报告:略
四,电商系统压测实战:
一、概括:
-
需求分析与测试设计(性能需求目标+业务模型拆解)
-
环境设计与搭建(尽可能接近线上真实场景)
-
测试数据准备(基于模型的数据准备)
-
性能指标预期(性能需求目标)
-
发压工具配置及脚本编写(压力策略)
-
测试过程(预计的前置准备过程,和压测时间点规划)
-
结果分析与测试报告
-
需求分析和测试设计:面试会问时间指标,容量指标,资源利用率指标这三者的关系,怎么样去定义或设置这些指标,什么样的性能测试是有效的,到了什么点可以停下来
下面是经典的性能测试的性能拐点图
- Number of concurrent users:并发用户/线程数:从小到大递增
- Utilization:资源利用率
- ThroughPut:吞吐量(tps/qps)
- Response Time:响应时间
从图来看,压测大致分成3个阶段
第一阶段:随着并发用户数递增,吞吐量/响应时间在可靠范围内,cpu资源伴随并发用户数进行递增,这些值处于合适范围内,我们可以在这个阶段获取容量规划的最优指标。
第二阶段:当资源利用率达到一定的限制,比如CPU/mysql达到95%以上,响应耗时的利用率大,会拖累响应耗时,这个阶段是系统风险的极限值。当然实际场景中这个阶段非常的短,很快就进入第三阶段。
第三阶段:压到峰值后,系统无法承受,出现mq积压/监控开始告警等情况,响应速度很慢,用户已经有感知
一、需求分析与测试设计
- 根据具体的性能测试需求,确定测试类型以及压测的模块(web/mysql/redis/系统整体)
- 前期要跟相关人员充分沟通,初步确定压测方案及具体性能指标
- QA完成性能测试设计后,需产出性能测试方案发送邮件到项目组,并且再次与相关人员沟通(或者组织性能测试评审),确认是否满足需求
二、 性能项目按目标场景分类:
- 新上线系统性能测试:要求容量测试,系统最大容量
- 系统升级性能测试类:和基线版本对比,性能不下降
- 新系统性能优化类:伴随调优目标的性能测试
本次实战中,我们可以定义为新系统上线的容量测试,目标为获取系统最大容量
测试场景:基准场景(单交易容量场景)
容量场景(递增场景、最大TPS,最快响应时间场景)
基准测试一般基于配置测试,通过配置测试得到数据,并将这个这个数据作为基准来比较每次调优后性能是否有所改善。
容量测试的目的是通过测试预先分析出反映软件系统应用特征的某项指标的极限值(如最大并发用户数、数据库记录数等),系统在其极限值状态下没有出现任何软件故障或还能保持主要功能正常运行
三、 性能指标的估算:
- 从友商处获取数据
- 根据线上数据进行估算:正常情况下产品不会直接给tps/qps,一般只会提供UV/PV 天,我们需要把这个拆解到小时/分钟级别。因为每天的流量不是平均的,如果我们要计算峰值,拿外卖举例,中午12点到2点,晚上6点到8点这4个小时是用餐高峰,占比80%,如果UV是100w/天,那我们最大的并发用户数(UV)=100w*80%/4*3600。比如商户页面流量有100w,那PV/天=100w/3600/24。
- UV:Unique Visitor独立访客,在一定时间内,访问网站的不同访客的数量,且每个访客只被统计一次。例如,假设用户周三访问3次,周四访问1次,则周三记为1次UV,周四也记为1次UV。
- PV:Page View页面访问量。网页被读者调用浏览的次数。网页每次打开或刷新一次页面,记录一次。用户对同一页面的多次访问,访问量累计
四、测试数据准备与构造:
- 接口请求参数:自己构造/日志获取/上下关联
- 数据表的数据填充
- 如果是多接口,则需结合业务场景设计请求比例
五、性能指标预期
- 每秒请求数(QPS)
- 并发数
- 请求响应时间(最小/最大/平均)
- 错误率
- 机器性能:cpu idle 30%、memory无剧烈抖动或者飙升
- 压测过程接口功能是否正常
不同性能测试方式下指标预期会有差异
六、工具选型/发压工具准备
1. Jmeter工具介绍
① 集成包,解压即可使用,windows、linux通用(依赖java环境)
② jmx脚本为xml文件,win、linux环境均可直接运行
③ 多线程并发
④ 运行完脚本会生成jtl日志,可在win环境界面工具中查看,统计
2. 脚本的编写
① http请求
② 其他
3. 命令:
- 启动压测: ./jmeter -n -t hb.jmx -l hb.jtl
4. 压测场景:
单接口/复杂事务->jmeter场景构造
5. 压力需求:
<1000 QPS or 万级以上 -> jmeter分布式支持
6. 是否周期性:
Jmeter jmx场景文件,数据驱动,结果落库
7. 二次开发需求:
Jmeter开源插件化思想,支持thrift,dubbo等多种协议,开源快速平台化
8. 问题支持:
Jmeter开放社区,广泛使用
七、压测过程说明/共识
- 测试前环境检查:记录机器参数
- 起压:根据被压情况,调节并发量到适合的情况
- 查看记录各项性能指标:
① nginx日志查看每秒请求数(或者k8s之类的)
② 查看nginx错误请求(如awk)
③ 查看机器参数:cpu idle,mem等
④ 查看db、cache等数据是否写入正常
⑤ 访问接口,查看功能是否正常
八、结果分析和测试报告
- 根据测试过程中记录的各项参数,结合压测工具产生的日志,对测试结果进行分析,并产出测试报告
- 测试完成后,及时与相关人员沟通,确认是否满足需求
- 发送测试报告邮件
二、 被测系统介绍:
- GitHub: https://github.com/macrozheng/mall
- 在线体验地址:https://www.macrozheng.com/admin/#/login
- 简介:mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现,采用Docker容器化部署。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
- 架构
- 整体服务存储分为:
Mysql:关系型数据存储,商品,订单等数据
Redis:缓存+部分存储,比如验证码。
MongoDB:例如用户关注品牌等存储
在压测过程中,对不同方式存储需要不同关注
三、压测目标预估
常见方式:线上取生产数据并统计
实际中也可以直接去nginx配置:
巧用shell技巧:cat var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -k 1 -nr
可以用这个拉出最常访问的接口
- awk ‘{print $7}’ :取出uri
- sort :把相同的uri聚合在一起
- uniq -c:统计每个请求的数量
- sort -k 1 -nr::按照第一列去排序
如果只有get请求,还可以使用这种方式:cat var/log/nginx/access.log | grep GET | awk '{print $7}' | > uri.txt
,然后再用csv的方式放在jmeter里面进行读取来做流量的请求,这样也比较快,不需要自己构造参数和url,相当于回放流量。post请求不适用
四、电商系统之简单场景:
- 从业务角度抽离基础登录场景:
- 用户登录
- 浏览首页:增加浏览记录(自动触发)
- 添加商品/购物车
- 下单,结算
- 退出(退出不代表登出)
- 初期拉新场景:用户注册功能
- 营销活动场景:优惠券领取功能场景
五、电商实战用户注册模块
- 用户获取验证码:GET /sso/GetAuthCode
- 使用注册码发起注册:POST /sso/register
- 用户使用匹配账户密码登录:POST /sso/login
- 其中接口场景比例1:1:1关系
- 获取的鉴权信息保存后为后续请求做测试数据支持
- 基础的jmeter UI功能配置演示:
http default header:header信息获取与设置
http default:做ip:host统一,便于替换
csv 参数化:请求的ip/host以及用户名/密码参数化做数据驱动
参数传递:后置处理器使用传递验证码到注册接口
使用前置处理器自动生成规范内测试手机号
cookie验证结果保存做数据构造
开始数据准备
- 用户获取验证码:GET /sso/GetAuthCode
- 新建一个接口
因为这边需要一个手机号,所以用随机生成的方式(添加-前置处理器-JSR233前置处理器)
ps:通常情况下,建议可以把手机号前3位设置为固定的,这样后期测试数据要删除也方便,然后参数引用时候只需要${phone_no}
如果我们需要调试,可以加一个debug sampler(添加-后置处理器-调试后置处理器)
在这里我们就能看到相关参数啦
如果接口的ip和端口号都一直的话,我们可以添加一个http信息头管理器(添加-配置元件-http信息头管理器),这样其他的接口如果不写ip和端口,默认就取这里的,这样后续修改也很方便
我们还可以继续做优化,把ip和port也参数化(添加-配置元件-CSV配置文件设置)
引用
添加前置处理器
执行
这样的好处是涉及到ip,host之类的只需要修改配置文件,后续想把jmeter文件做成平台化的话,可以很好的进行数据驱动
接着我们进行登录
- telephone:沿用上面的phone_no
- username:
- password:这2个字段可以提前生成一批,也可以用counter的方式进行生成:thread group->add->config element->counter
这边还可以增加一些全局的用户变量前缀,然后再进行引用
引用:
- authCode:这个字段需要从上个接口的response获取,我们这边用json提取器获取:add->post processors-> json extractor,然后再进行引用
验证是否成功:additionally assert value也要勾上
接下来进行登录
参数和注册时候保持一致
请求结果
我们需要获取里面的token,所以我们先添加一个json后置解析器
把数据存起来
然后我们可以设置线程数之类的,正式跑起来啦~
当然我们跑还是用命令行跑,只有调试才用界面
总结
提问环节:
- 如何通过前置处理器快速生成MD5?
我们可以去maven仓库下一个对应的依赖(commons-codec 1.3/1.4),然后放到jmeter对应的库下,然后直接使用即可
- 如果签名需要用到整个请求的body做加密,这个要怎么做?
比如我们要获取temp变量
我们直接get,然后塞到md5进行加密即可
String temp=var.get("temp");
DigitalUtils.md5Hex(temp);
- 每个接口都有加密,但是加密会损耗机器性能,该怎么做?
- 看加密的实时性要求,如果要求不高,可以通过参数化的方式前置生成的string,相当于csv方式读取结果
- 和研发配合,打Test标识,当被测服务碰到这个标识,直接通过,不进行校验(如果研发不配合,可以告知如做了可以达到10w的qps,没做的话只能达到3w的qps,如果不做改动需要加几台机器,每个月要耗费几万块钱。沟通时候要拿着现在做的准备,和尝试过的解决方式以及解决方式带来的影响/投入回报比,这样沟通起来会顺畅很多)
- 万能token
六、报告的展示
略,详见之前文章 戳这里
七、电商实战之日常场景
我们可以先跟pm确认具体的场景,然后再通过抓包或跟研发确认的方式确认下要压测的接口。
浏览首页4个接口的预估占比分别是2:4:2:1
登录在电商场景其实不太重要,通常需要的是登录后保存下来它的cookie或者token啥的,之前我们已经通过脚本把它存在某个目录下
浏览首页涉及2个点:登录鉴权,流量控制。
首先,我们来获取token,新建一个线程组,再新建两个事务控制器(事务中会包含一个或多个请求,当含有多个请求时,想看一个事务的测试结果(所有请求的总时间和总的吞吐量等),可以通过事务控制器进行操作。)
因为这次登陆验证是放在header里,所以我们新建一个header manager来获取鉴权信息
然后我们通过csv的方式读取之前存下来的token
新建homeContent(访问首页)接口,然后放到HomePageView下面
首页下面可能会有很多分类,1次首页访问我们可能会访问2次商品分类,我们怎么控制访问首页和访问商品列表是1:2的关系呢?这个时候我们可以使用Loop Controller(逻辑控制器-循环控制器)
然后我们在这个控制器下新建获取首页商品分类请求
这里需要传获取parentid,不建议写死,这里采用从数据库获取数据,然后放在文本上,然后直接放到csv里进行读取的方式(或也可以从nginx里面获取)
建议所有请求后面都加一个校验
接着,我们新建分页获取推荐商品的
接着,我们再新建点击分类专题页的请求,因为这个我们模拟的是人数比其他三个请求少一半,我们采用逻辑控制器的方式来做
接着我们再新建请求(其中cate_id我们就跟上面一样,直接从数据库读取,然后放在csv进行读取)
接着,我们正式开始压测
我们重点关注错误率和响应时间,确认要停止还是继续压测
下面,来看添加购物车操作
我们先新建一个事务服务器
再在底下新建一个请求
然后修改下
准备测试数据
以csv的格式导入
我们可以通过看log定位问题
下面我们来刷新购物车
添加校验
添加订单
获取会员的订单列表
根据购物车信息生成订单信息
造数据生成收货地址
创建确认单
取出里面的id
根据购物车信息生成订单
提取orderid
支付成功的回调
添加校验
至此整个流程串起来了,可以开始压测查看效果
我们加压的时候一般采用递增式加压,所以这边可以用utimate thread group的方式进行加压
我们先新建这个,然后把之前的接口全部放到它的目录下
设置参数
后面跳转了下5个参数,这个为准
字段 | 含义 |
---|---|
start thread count | 初始线程数 |
initial delay. sec | 延迟时间 |
startup time.sec | 开始时间 |
hold load for.sec | 持续时间 |
shut down time | 结束时间 |
后台执行程序(执行前记得关闭查看结果树之类的选项,避免影响结果)
执行前记得把recycle改成true
刚刚配置的参数也改下
然后我们可以开始跑
看看压测情况
因为压测时候我们关心是最近的数据
17:03之前5个并发用户已经可以承载最大量级
响应耗时会非常高,而且错误率已经很大了,我们确认下这个错误率是否在承受范围内,不是的话要快速停下来
已经达到2s
整个性能曲线已经很差
五. 客户端演练总结
在4-5个并发的时候系统就已经达到了系统极限,在往上并发用户增加时响应耗时没有增加,错误率开始大大增加,达到了性能拐点,我们在17.03:30s的时候,user是3的时候,系统还比较稳定,后面错误率在03:20s的时候大大增加,系统超过限制,错误的接口主要在orderpay,order,generateOrder接口,主要集中在订单服务上。首页之类的主要是数据库读的操作,对性能要求不大。cartItem不需要校验是否成功插入,更多是覆盖的操作,对数据库压力较小。压力主要集中下单操作,一是订单流程长,二是涉及多个协调动作。从耗时结果看,耗时主要发生在generateOrder,达到2s以上,HomePageView和HomeContent也会随之增加,这两个和订单用了同一个数据库,数据库一致情况下,整个数据库的响应都会变慢。
下面是jmeter其他功能的一些介绍
1、如何达到一定错误率后停止压测?
jmeter有个插件
- 平均响应时间超过2s,持续10s以上
- 平均延迟大于1s,持续10s以上
- 错误率10%以上,持续10s停止
2、性能测试环境搭建?
答:最好配置跟线上类似或者等比缩放
3、错误率最好是多少?
答:不超过1%