原文链接:https://mp.weixin.qq.com/s/9PxSPuHmucSLi_welq6uNQ
现在性能测试工具太多,根据业务不同使用,比如说我们熟悉的loadrunner、jmeter、ab、webbench等等,这些工具是也主流大部分也在使用,但是如果你能看懂python代码,会写就更好了,可以根据自已的业务编写,不会自己写框架也可以尝试一下今天的主角Locust,一款基于python的开源性能测试工具,主要也是负载测试工具,它的优点是学习起来比较简单,功能完全自定制,自由控制业务逻辑,使用比较灵活,支持分布式。
它主要就是模拟一群用户将访问你的后台。每个用户的行为由你编写的python代码定义,同时可以从Web界面中实时观察到用户的行为。
Locust完全是事件驱动的,因此在单台机器上能够支持几千并发用户访问。
与其它许多基于事件的应用相比,Locust并不使用回调,而是使用gevent,而gevent是基于协程的,可以用同步的方式来编写异步执行的代码。每个用户实际上运行在自己的greenlet中。
所有的性能测试工具都至少包含这3方面:
1、压力产生器,也就是可以指定产生多大的压力,多少并发;
2、数据统计,也就是结果的展示,要统计TPS【QPS】是多少,响应时间多少等等,这些数据;
3、代理功能,代理功能简单来说就4个字,分摊压力:如说你压测的时候要用1000个并发,但是你的电脑(压力机)配置比较弱,只支持500并发,再大电脑就死掉了,完犊子,压测不了。那怎么办呢,就得分摊压力,从其他服务器发压力,那就可以了,每台服务器上500个并发,人多好干活嘛,代理就是干这个的,把这个上面的压力分摊到别的电脑上。
当然今天的主角Locust这3个功能都是有的,locust的官网是 www.locust.io,有详细的文档查看。
Locust其实是python的一个第三方模块,安装很简单,直接pip install locust即可,或者自己下载安装包,手动安装。安装完成后,就有locust命令,在命令行里面输入 locust --help,有帮助信息就安装成功了。
如何用呢,很简单,只需要几行代码就可以实现,并且有漂亮的web界面也可用命令行执行,可以设置并发数,和查看结果,当然缺点也是有的,就是没有资源利用率统计,需自行在被测服务器中监控。
locust控制台页面:
开始测试后的页面:
tps图和响应时间图:
先写几行简单的代码,写一个打开BestTest首页的脚本。
from locust import HttpLocust, TaskSet, task
#HttpLocust 这个类的作用是用来发送http请求的
#TaskSet 这个类是定义用户行为的,相当于loadrunnerhttp协议的脚本,jmeter里面的http请求一样,要去干嘛的
#task 这个task是一个装饰器,它用来把一个函数,装饰成一个任务,也可以指定他们的先后执行顺序
class BestTest(TaskSet):
#自己定义的类,继承TaskSet,也就是这个类是实现咱们要去请求什么的
@task#用task装饰器把这个函数装饰成一个咱们要执行的性能任务
def index(self):#这个函数里面定义的是咱们要具体做的操作
self.client.get('/')#请求这个url里面的哪个路径,如果是接口的话,就是哪个接口
class BestTestIndexUser(HttpLocust):
#这个类继承了HttpLocust,代表每个并发里面的每个用户
task_set = BestTest #这个是每个用户都去干什么,指定了BestTest这个类,它就会每个用户去运行besttest这个类里面的方法
if __name__ == "__main__":
import os
os.system("locust -f ./load_test.py --host=https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew")
或者注释掉程序入口代码,直接命令行执行下面
locust -f besttest.py --host=https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew
#-f是指定一个python文件 后面跟上咱们刚才写的python文件
#--host是你要访问哪个后台,后面跟的url
默认端口号8090,启动locust:运行完之后,访问的时候用ip:8090就可以访问了.
刚才上面写的例子是单个接口压测, 或者更说单场景的,如果想做混合场景的压测。
只需要写多个task就可以了,也就是在类里面写多个函数,想谁想执行,标上数字就行了,1,2,3,4代码如下:
不同类型的模拟用户
1.考虑一种测试场景:如果我们想测试前端用户登录和详情,需要并发100个人,并且使其中10个人登录,另外90个人在详情页瞎逛,用locust框架应该如何实现呢?框架的命令行有提示locust [options] [LocustClass [LocustClass2 …]]
当我们使用框架运行一个测试脚本时,如果脚本中只有一个Locust类,则框架会以这个类作为模拟用户的模板;如果该脚本中不止一个Locust类,则需要指定类名;如果需要模拟多种类型的用户,则可以指定多个类名,框架会根据每个Locust类的权重分配模拟用户的比例(默认权重为10)。
一个简单示例如下:
from locust import TaskSet, HttpLocust, task, TaskSequence, seq_task, InterruptTaskSet
class LoginTaskSet(TaskSet):
@task
def login(self):
print("登录")
class DetailsTaskSet(TaskSet):
@task
def details(self):
print("详情页")
class UserOwn(HttpLocust):
weight = 10
task_set = LoginTaskSet
host = "https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew"
min_wait = 10000
max_wait = 10000
class UserTwo(HttpLocust):
weight = 90
task_set = DetailsTaskSet
host = "https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew"
min_wait = 10000
max_wait = 10000
命令行运行:locust -f ***.py UserOwn UserTwo
如果并发10人,就会有1人在登录,9人在详情页瞎逛了,业务是自己编的
2.TaskSet的tasks属性
定义任务的方式,就是在TaskSet类中定义一个方法,并标注其为@task;另一个定义任务的方式就是使用TaskSet的tasks属性,这个属性值可以是数组或字典,其中表示的任务可以是一个函数,也可以是另一个TaskSet。如果使用数组,其元素可以是元组(callable, int),int值即为该任务的权重。
2.1函数
示例中的tasks = [(play_one, 5), play_two]与tasks = {play_one: 5, play_two: 1}是完全等价的。tasks中的函数和有注解@task的函数有同等的地位,都是这个TaskSet的一个基本单位。
#函数需有一个参数表示self
def test_one(self):
print("test_one")
def test_two(self):
print("test_two")
class TestTaskSet(TaskSet):
tasks = [(test_one, 5), test_two]
@task
def test(self):
print("the test is me")
class TestTwo(HttpLocust):
weight = 90
task_set = TestTaskSet
host = "https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew"
min_wait = 100
max_wait = 100
2.2嵌套TaskSet
tasks属性的元素也可以是另一个TaskSet,这种做法就会形成嵌套的子任务。例如:我们测试前端,需要每一个用户在详情页瞎逛的时候,有1/10的概率进入详情页进行另外的操作,详情页逛完退回主页继续闲逛,示例如下:
class TestTaskSet(TaskSet):
tasks = [test_one, test_two]
@task(5)
def test_details(self):
print("the details is Wandering")
@task
def end_details(self):
print("details end")
self.interrupt()
class TestOutTaskSet(TaskSet):
tasks = {TestTaskSet: 1}
@task(9)
def Test_out(self):
print("the details is out")
当进入嵌套的任务集执行任务后,会根据嵌套任务集的任务权重执行任务,除非调用self.interrupt()或抛出中断异常raise InterruptTaskSet否则将一直在子任务集中执行下去
3.内部类
还有一种实现嵌套子任务的方式是使用内部类,如上诉示例,等同于:
class TestOutTaskSet(TaskSet):
@task(9)
def Test_out(self):
print("the details is out")
@task
class TestTaskSet(TaskSet):
tasks = [test_one, test_two]
@task(5)
def test_details(self):
print("the details is Wandering")
@task
def end_details(self):
print("details end")
self.interrupt()
4.类的继承
如果一个TaskSet的父类是另一个TaskSet,那么父类的所有任务会同样的成为子类的任务。
5.最后尾声
Locust类是创建模拟用户的模板,所以如果我们要使用不同的角色进行测试时,就需要给框架提供不同的Locust类。TaskSet类是模拟需要执行的任务集,我觉得可以这么理解,这是一个任务的池子,里面的每个单位都是一个任务,这个任务可以是函数也可以是另一个TaskSet,这个任务的定义方式有几个(当前TaskSet通过@task定义,继承父类的任务,tasks属性中一个元素),但它们都是同等地位的,执行的概率与它们的权重相同,每一次模拟用户都会从这个池子里挑选任务进行执行(直到stop_timeout或页面点击Stop)。当挑选到一个类型为TaskSet的任务执行时,就进入了嵌套的子任务当中,相当于到了子任务的池子里开始挑选任务执行(直到调用self.interrupt()或抛出中断异常raise InterruptTaskSet)。
Bye-bye 下期见!!
快来关注公众号【自动化测试交流圈】吧,下期内容更精彩哦!!
或直接加入微信【微信圈子】:入口->搜一搜->圈子:搜索【自动化测试交流圈】