好用的python性能测试神器–Locust

原文链接: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控制台页面:
好用的python性能测试神器–Locust_第1张图片
开始测试后的页面:
好用的python性能测试神器–Locust_第2张图片
tps图和响应时间图:
好用的python性能测试神器–Locust_第3张图片
先写几行简单的代码,写一个打开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 下期见!!
快来关注公众号【自动化测试交流圈】吧,下期内容更精彩哦!!
或直接加入微信【微信圈子】:入口->搜一搜->圈子:搜索【自动化测试交流圈】

你可能感兴趣的:(性能测试)