开展性能测试你不知道的秘密!
如何开展性能测试开展性能测试一般有以下几个步骤:确定性能需求、设计测试用例、准备测试环境及测试工具、执行测试、分析结果、性能调优、编写测试报告。
性能需求
我们先讲解第一步,如何确定性能需求?还是讲一个故事:
某货运公司提出一个需求,需要制造一辆卡车用于A城市和B城市之间的货物运输。某汽车制造商制造了一个可以载重5吨的小卡车去交付,交付现场发现货运公司需要拉的货物重达50吨。于是汽车制造商只能重新制造,20天后终于生产出了可以载重达标的货车,于是货运公司进行了验收试验,拉上货物从A城市到B城市,沿途需要经过一个陡坡,这时才发现载重50吨的货车想要爬上陡坡需要至少250匹的马力,但是该货车设计的马力只有190匹,完全不能实现从A城市到B城市的运输,只能再次重新生产。
上面这个故事我们可以看出,载重50吨、马力250匹这两个就属于性能需求。因为如果不能满足这两个性能要求,就不能帮助货运公司实现A城市和B城市之间的货物运输。那么,我们总结一下性能需求需要满足的特点:
明确需要何种测试类型
上面我们提到,性能测试包括负载测试、压力测试、峰值测试等类型。所以在做性能测试的时候,首先需要明确测试类型,不同场景需要用到不同的测试类型,不同类型的测试方法也有差异,可以选择其中一种类型或者多种类型的组合。上面的故事中需要验证车辆的最大性能拐点和长时间运输的性能效果,所以至少需要用到负载测试和峰值测试。
需要满足最低运行标准
这里不难理解,上面的故事中汽车制造商制造了三次货车才满足了货运公司最基本的运输需求,就像做软件一样,如果性能需求不足以满足客户的最低要求,只能返工重做。
需要有明确的数字支撑
作为一个性能需求,必须是明确的数字,就像载重50吨、马力250匹一样,只有约定了明确的数字才能做出合格的产品。在软件测试中,某个接口要求响应时间在200ms以内,TPS需要达到1000以上。只有明确了数字,才能更好的展开性能测试。
涉及的相关人员需要达成一致
性能需求必须保证相关人员的意见是一致的,上面的故事中货运公司和汽车制造商需要保持卡车的性能需求的同步和一致性。在软件测试中,性能需求需要从产品、研发、测试、运维等相关的人员保持一致。
那么,获取性能需求的途径都有哪些呢?
需求阶段
就像上面的故事,如果在需求阶段就充分了解了客户的性能需求,就不会返工3次。在实际工作中,一个需求到手以后,需求的提供方不能完全预估出性能的要求,但是作为技术人员需要充分了解需求,再根据需求定义出性能需求。
行业的最低标准
每个行业都会有众多的公司,每个公司一般也都会有同类的产品,如果想要在众多产品中脱颖而出,最低的标准就是不能比别人差。一旦你的响应速度比别人差很多,是很难留存用户的,根据友商的各种数据可以定义出一些性能需求。
公司内部标准
公司内部会有一套自己的性能标准。根据公司现有产品的运行数据进行分析,总结出一套公司标准的数据作为性能需求。
测试工具
在日常的软件测试工作中,最常用的性能测试工具是LoadRunner和Jmeter。
LoadRunner 是HP(Mercury)公司出品的非常有名的商业性能测试工具,支持多种协议,功能非常强大。支持脚本录制和自主编写脚本,如果想要更充分使用它的功能,对使用者的要求比较高,目前大多介绍性能测试的文章都以该工具为基础。
Jmeter 同样是非常有名的开源性能测试工具,是由Apache组织基于Java语言开发,功能也很完善。一般在介绍Jmeter的时候,都是作为接口测试工具的使用,但实际上,它是一个标准的性能测试工具。它相关的资料也非常丰富,官方文档也很完善。
另外,还有一款很好用的开源性能测试工具:wrk。也许很多人都没有听说过,他是一款轻量级性能测试工具,采用网络异步IO模型,使得系统使用很少的线程模拟大量的网络连接以增大并发量,只支持http协议类型请求,官网文档是这样描述的:
但是,雪球在做性能测试的时候,用的最多的却不是以上3款工具,而是另外一款开源性能测试工具:Locust。下面先通过一组对比简单了解一下这几款工具。
根据上面的对比,我们可以看出Locust的几个特点:
Locust使用Python语言开发,测试资源消耗远远小于Java语言和C语言开发,且HTTP请求完全基于 Requests库,同样可支持其他协议或者自定义请求,可拓展性强,理论上可测一切系统;
Locust借助于协程实现对用户的模拟,不同其他三款使用线程数提高并发量,相同物理资源(机器cpu、内存等)配置下Locust能支持的并发用户数相比另外3款可以提升一个数量级;
Locust支持分布式部署测试,能够轻松模拟百万级用户并发测试。
同样,Locust也是有很多缺点的:
不支持脚本录制,想要使用就必须自己编写测试代码,这对于不太了解代码的人来说,算是一个很高的门槛了;
不支持资源监控,想要监控资源需要借助其他监控的工具;
极简风的测试报告与分析,也是一直为人所诟病。
雪球选择Locust的主要原因,就是看中了它的这几个优点。雪球用户基数大,有些接口的QPS已经上万,刚好Locust可以满足少量测试资源即可模拟大量并发。对于它的这些缺点,都可以通过其他方式进行弥补。另外,使用Locust作为性能测试工具,还需要注意以下几个方面:
网上搜索的Locust的教程,大部分都是基于0.x的版本进行编写的,但是现在的最新版是2.x的版本,这两个版本是有很多区别的。
既然Locust是借助于协程实现并发,就会受到协程带来的一个重要的问题:失去了标准线程使用多CPU的能力,也就是说一个Locust的进程只能使用一个CPU线程的资源,如果想利用多核多线程的CPU资源,就需要同时启动多个Locust进程,就需要用到master/slave的机制。
运行规模测试时,建议在Linux机器上执?此操作,因为协程在Windows下的性能很差。
执行测试
首先需要安装Python3.9+Locust2.9的环境,下面是一个简单的测试脚本:
from locust import HttpUser, TaskSet, task, between, User
class ReplayAction(TaskSet):
@task(8)
def demo(self):
url = '/S/SH600519'
headers = {'Content-Type': 'application/json; charset=UTF-8'}
try:
http_url = User.host + url
with self.client.get(http_url, headers=headers, name=url, catch_response=True) as response:
if response.status_code != 200:
response.failure("Failed")
except Exception as e:
print("出现异常:%s" % e)
class RePlayer(HttpUser):
wait_time = between(0, 0)
host = "https://xueqiu.com"
tasks = ReplayAction:这里定义要压测的接口,继承TaskSet。接口用@task进行装饰,表?为?户?为。根据不同的性能测试需要,这里可以定义多个接口,@task括号??参数表?该?为的执?权重,数值越?,执?频率越?,不设置默认是1。self.client调?get和post?法,和requests类似。在特定的需求下,ReplayAction可以有多个,每个ReplayAction里面装饰的权重分别计算,互相不影响。
RePlayer:继承HttpUser(老版本继承HttpLocust),?于设置?成负载的基本属性。wait_time是每个负载任务之间最小等待时间和最大等待时间(老版本需要单独设置,分别是min_wait和max_wait);tasks指向定义了用户行为的类,它是列表格式,可以有多个ReplayAction(老版本为task_set,不是列表格式,只能定义一个ReplayAction,如果想要实现多个的话,需要在ReplayAction中定义tasks,并做task嵌套来完成,如果实现了task嵌套,interrupt()在task class中必须定义,否则进来就出不去了。)
脚本编写完成以后,就需要执行脚本,雪球有专门用于执行性能测试脚本的Linux服务器。将脚本上传至服务器以后,执行以下命令:
locust -f demo.py --headless-u 1000 -r 100 -t 3m
--headless:非Web UI的方式执行脚本,结果直接在命令行输出(老版本为--no-web)
-u:并发用户数(老版本为-c)
-r:每秒增加的用户数(Locust不是启动后马上就通过设定的并发用户数执行压测,而是逐渐加压,直到增加到设定的并发数为止,这个设置就是每秒加压的数量)
-t:设置运行的时间
由于Locust是借助于协程实现并发,所以通常情况需要用到分布式进行压测,分布式的主从启动脚本略有不同:
Master:
locust -f demo.py --master --headless-u 1000 -r 100 -t 3m --expect-workers=1Slave:
locust -f demo.py --worker --headless-u 1000 -r 100 -t 3m --master-host=xx.xx.xx.xx
--master:表示该进程是主进程,需要优先启动主进程,启动以后主进程进入等待状态,直到连接了指定量的从进程以后才开始工作。
--expect-workers:表示等待几个从进程连接后再开始测试,从进程个数达到要求后会立即开始执行,不设置默认为1(老版本为--expect-slaves)。
--worker:表示该进程是从进程(老版本为--slave)。
--master-host:表示连接主进程的IP,不设置表示连接本机
等待程序执行完成后,命令行会显示本次执行的结果,如下:
性能结果
性能测试步骤中,还有一个环节是非常重要的,那就是分析结果环节,下面我们重点讲一下性能结果都有哪些需要注意的。
相信大家都吃过国内很出名的火锅连锁某某捞,大部分人在用餐的时候应该都需要排号等位吧,我最长一次排了近3个小时。大家有没有想过,如果一个面馆,让你排队三个小时才能吃上一碗面,你应该转头就走了,为啥某某捞就能心甘情愿的排队三个小时呢?
上面的问题先思考着,下文都简称一个餐厅,我们先做一些假设:
餐厅共有10张桌子,10个服务员,每个服务员负责服务一张桌子的客户;
每桌人用餐时间都是1小时;
顾客忍耐的最长等待时间是2小时。
当餐厅只有2桌人用餐的时候,只需要2个服务员提供服务即可,其他服务员有的在休息,有的在刷手机。1小时后,两桌客人吃完饭走了,在这一小时里整个餐厅只赚了2桌顾客的钱。
当餐厅有7桌人用餐的时候,就需要至少7个服务员提供服务,其他服务员需要临时打打杂。1小时后,这7桌客人也吃完饭走了,那么在这一小时里整个餐厅赚了7桌顾客的钱。
由于这个餐厅有10个服务员,所以当餐厅有10桌人用餐的时候,10个服务员都需要去提供服务,餐厅在这1小时里可以赚10桌顾客的钱。
通过上面的几个场景,我们可以看出,同样是一个小时,餐厅可以根据顾客的数量不同来赚取不同的钱,随着顾客人数增多,餐厅的工作效率也是逐渐提高,但是每桌顾客的用餐时间都是1小时。
一个新的场景出现了,本来餐厅一个人没有,但是突然之间来了11伙人用餐,前10桌人分别进入餐厅用餐了,剩下这一伙人由于没有餐桌和服务员,只能排队等位,等到那10桌吃完以后才能用餐。前10伙人的用餐时间依然是1个小时,但是最后这一伙人的用餐时间变成了等位1小时+用餐1小时,也就是2小时。
本餐厅的生意非常火爆,突然之间来了35桌人用餐,前10桌人进去用餐,其他25桌人只能外面排号,根据上面的情况我们不难算出,排号1-10的人,他们用餐时间是排号1小时+用餐1小时,也就是2小时。排号11-20的人,他们用餐时间是排号2小时+用餐1小时,也就是3小时,同理排号21-25的人,他们排号的时间就要3个小时,已经超过了忍耐的最长时间,于是他们愤怒的走了。
看到了这里,大家是不是觉得餐厅的排号等位有点像性能测试的场景,我们根据以上的场景提取一些关键性的名词解释吧。
吞吐量:单位时间内处理事务的数量。通常单位时间都按1秒统计,也就是性能测试中经常提起的名词:TPS(每秒处理事务的数量)。通过上面的场景,我们可以计算出该餐厅的最大吞吐量为:10桌/小时。
响应时间:指某个请求或操作从发出到处理,再到接收到反馈所消耗的总时间。通过上面的场景,我们可以认为用餐时间就是响应时间,直接就餐的人响应时间就是1小时,排号1-10的人响应时间就是2小时,以此类推。
平均响应时间:每个请求的响应时间之和除以总请求次数就是平均响应时间。这个指标也是目前大多公司做性能测试中的主流指标之一。
最小响应时间:所有请求中,最小的响应时间。
最大响应时间:所有请求中,最大的响应时间。
90%响应时间(P90) :这个指标在性能测试中经常被大家忽视,不知道这个指标是用来干什么的,甚至不知道这个指标是如何计算的。它的计算规则其实很简单,总请求中按照响应时间从小到大排序,取前90%请求的平均响应时间就是这个指标的值。到现在是不是还不知道有什么作用?
通过上面的图,我们可以看出,有两组测试数据,他们的平均响应时间都是7,那么你们认为哪次结果更理想呢?现在我们通过上面的计算方式,计算一下90%响应时间。
通过上图看出,我们去掉了最后10%的数据,然后再求平均值,这个时候我们发现,90%响应时间分别是5.78和6.78,是不是发现这两次的数据有明显的差距,这能说明什么呢?
假如在一次实际的性能测试中,某接口请求了1万次,测试结果中最小响应时间为12ms,最大响应时间为15s,平均响应时间为1.1s,90%响应时间为500ms,我们可以看到最小响应时间和最大响应时间的偏差非常大,这时候我们是不是对平均响应时间的指标数值表示怀疑呢?
再来一个例子,东奥会刚刚结束不久,我们在看比赛的时候,经常听到类似的话“去掉一个最低分,去掉一个最高分,该选手最终得分为”。这句话在各大赛场经常能听到,为啥要去掉一个最低分和最高分呢?因为这样可以去掉极端数据对选手得分的影响,能够尽量保证选手所得分数反映该选手的正常水平。
再回到这个问题上,90%响应时间这个指标其实是验证平均响应时间的准确性的,指标越接近平均响应时间,越能证明平均响应时间的准确性和可靠性。那么又有人要问了,直接用这个指标替换平均响应时间是否可以?答案是否定的。还拿上面的例子来说,15s的最大响应时间它是真实存在的,不能忽略该请求对整个系统的影响。那么就引出另一个问题了,如果90%响应时间和平均响应时间差距较大该如何处理?
再回到餐厅等位的问题上,假如有一伙人他们到餐厅排一个号以后就去商场逛街了,逛完街又去看了场电影,看完电影后才回来吃饭,前前后后这伙人从排号到吃完饭用了7个小时,如果这时候计算餐厅一天的平均用餐时间,会因为这一个场景影响整个数据的准确性。我们可以通过两种办法解决这个问题:第一种办法,我们可以计算前几天每天的平均用餐时间;第二种办法,我们可以取之前一个月的所有数据放一起做计算。
看到这里大家是不是明白了呢?在性能测试中,我们也是通过两种情况解决这个问题:第一种,同样的场景多次运行取平均值;第二种,增加运行事务的总数,减少特殊情况对整个数据的影响。
并发用户数:指的是系统可以同时承载的正常使用系统功能的用户的数量。
最大并发用户数:指的是系统可以同时承载的正常使用系统功能的最大用户数量。在上面餐厅的例子中,最大并发用户数就是30。因为如果同时来了超过30人,就必然会有人因为等位时间太长而离开。在性能测试的场景中也是一样,如果超过了最大并发用户数的极限,就会出现响应超时、响应错误、甚至系统崩溃的风险。但是在实际的性能测试场景中,我们都会根据有效的性能需求计算最大并发用户数。比如要求接口响应时间在1秒以内,这时候的1秒就是有效的性能需求。再回到餐厅的例子中,如果要求用户用餐时间在2小时内的最大并发用户数,那么就应该是20,因为一旦超过20就必然会有人用餐时间大于2小时。
最佳并发用户数:这又是一个经常被大家忽视的性能指标,他指的是系统没有资源的浪费,整体效率最高的时候同时承载的用户数量。还是拿餐厅为例子,这个餐厅的最佳并发用户数就是10,因为每小时来10桌人的话,没有人需要等位,并且没有空闲桌子和空闲服务员。
但是往往在实际中这个指标是一个范围,这又是为啥呢?餐厅的服务员也会因为长时间工作导致疲惫,顾客用餐完毕后还需要撤掉餐具,收拾餐桌等工作,所以就不能保证全天候的用餐时间都是1小时,会有一些其他事情导致的时间损耗。所以根据日常的规律,我们可以给出餐厅的最佳并发用户数是9-10。下面我们再看一张图:
这张图是一次真实的性能测试数据分析图,在这张图中我们可以看到,最开始,随着并发用户数的增长,总时间和TPS会相应的增长,但是平均响应时间的变化不大;不过当并发用户数增长到一定程度后,TPS增长明显放缓甚至停止增长,而平均响应时间却进一步延长。如果并发用户数继续增长,TPS已经开始下降,平均响应时间急剧增加。
根据上面的图,我们把并发数分为4个区间:
50以内,这个区间叫较轻压力区间。在这个区间系统资源利用率低,系统响应非常快,所以TPS增长和并发数成正比。
50-100,这个区间叫舒适压力区间。在这个区间的并发数就叫做最佳并发用户数,在这个区间系统的整体效率最高。
100-120,这个区间叫较重压力区间。在这个区间TPS已经开始下坡,平均响应时间开始上升,系统虽然可以正常工作,但是用户的等待时间延长,满意度会开始降低。
120以上,这个区间叫严重压力区间。在这个区间明显看到TPS向下的拐点,平均响应时间也急速上升,在这种情况下,会导致有些用户无法忍受等待的时间而放弃。
因此,在性能测试中,最佳并发用户数往往才是更应该考虑和关注的,这个指标更能反应系统的性能数据。
上面的图,还有一个经常被大家忽略的指标,或者根本没有考虑过的指标,我叫它性能面积,把每个区间的TPS和响应时间框出来的面积就叫性能面积。那么性能面积如何反应出系统的性能呢?
较轻压力区间的性能面积越大,说明系统的处理能力越强。
舒适压力区间和较重压力区间的性能面积越大,说明系统的稳定性越好。
严重压力区间的性能面积越大,说明系统的容错能力越强。
上面还有一个问题没有讨论,为啥某某捞就能心甘情愿的排队三个小时呢?大家在某某捞排队都在干什么呢?我来给大家列一下:点菜、吃小吃、做美甲等等。大家有没有发现,等位的时候做一些其他事情,既可以打发时间,又可以为后续的用餐提供准备。点菜本来是用餐的第一个环节,我们在等位的时候做了就可以减少用餐的时间;吃小吃可以让你有一定的饱腹感,进一步减少用餐时间。其实这个也能引出一个和性能相关的概念--分步加载和预加载。这个技术主要应用于前端的性能优化。用户在请求某个前端页面时,如果不能保证页面所有元素都瞬间加载出来,可以先加载出部分内容,其他内容逐步加载;当用户频繁访问同一个页面的时候,可以把一些静态的资源储存到本地,以后这些资源可以直接从本地获取。
总结
本文章简单的描述了如何进行性能测试,其中最主要讲述了在性能测试中经常被大家忽视的点。首先性能测试越早开展越好,如果可以配合开发在Coding环节就介入测试效果会更加明显。其次性能问题千万不要忽视中间件的上下游,往往这种地方出问题会更加致命。另外在结果分析中要善于应用P90等指标辅助分析结果,让测试结果更加趋向真实。
页:
[1]