性能测试工具Locust的介绍和使用

内容来自网络html

https://www.w3xue.com/exp/article/20191/16707.htmlnode

https://blog.csdn.net/qq_36255988/article/details/82622044python

1、Locust描述nginx

(1)git

locust是一个易于使用的,分布式的,用户负载测试工具。用于web站点(或其余系统)的负载测试,而后算出系统可以处理多少并发用户。
locust的思想是:在测试期间,一大群"蝗虫"会攻击你的网站,每个"蝗虫"的行为都是由你本身定义的,同时,能够在一个web界面上实时的监控这群进程。这会帮助你更好的"进行战斗",在真正的用户进入以前,就找出代码中的瓶颈。
locust彻底是事件驱动的,所以它可以在单机支持数以千计的并发用户,相比许多其余的基于事件的应用,locust不使用回调函数。它使用轻量进程---gevent。每个访问你的网站的locust实际上都在它本身的进程内部运行(准确地说,是greenlet),也就是咱们一般说的协程。这容许你在不使用带回调函数的复杂代码的情形下,使用python写出很是具备表现力的脚本。
(2)
Locust是开源、使用Python开发、基于事件、支持分布式而且提供Web UI进行测试执行和结果展现的性能测试工具。而它之因此可以在资源占用方面明显优于JMeter,一个关键点在于二者模拟虚拟用户的方式不一样,JMeter经过线程来做为虚拟用户,而Locust借助gevent库对协程的支持,以greenlet来实现对用户的模拟,相同配置下Locust能支持的并发用户数相比JMeter能够达到一个数量级的提高。
Locust使用Python代码定义测试场景,目前支持Python 2.7, 3.3, 3.4, 3.5, 3.6和3.7。它自带一个Web UI,用于定义用户模型,发起测试,实时测试数据,错误统计等。
当前最新发布版本v0.11.1,还提供QPS、评价响应时间等几个简单的图表。

优势:github

1. 易用。很方便地基于Python进行脚本扩展和业务请求实现。golang

2. 彻底基于事件驱动,因此不受进程和线程的限制,能够支持发起更高的并发数请求。web

3. 能够分布式发起并发请求算法

4.Locust有一个整洁的HTML+JS的用户界面,实时显示相关测试细节。因为用户界面是基于网络的,它是跨平台的和容易扩展。json

5. 开源。

缺点:

1. 图表相对loadrunner 比较简单。(在Linux 下部署时能够看到图表,在Windows 下没有)

2. 不支持监控被测机,须要结合nmon等工具辅助监控。

2、Locust安装

1.一、   --->  pip3 install locust 

1.2 、 经过GitHub上克隆项目安装(Python3推荐):https://github.com/locustio/locust  ,而后执行     ...\locust> python setup.py install

 二、安装 pyzmq

 If you intend to run Locust distributed across multiple processes/machines, we recommend you to also install pyzmq.

 若是打算运行Locust 分布在多个进程/机器,须要安装pyzmq.

 经过pip命令安装。 />  pip install pyzmq

三、安装成功,CMD敲入命令验证。 /> locust --help

 1 Options:
 2   -h, --help            show this help message and exit
 3   -H HOST, --host=HOST  Host to load test in the following format:
 4                         http://10.21.32.33
 5   --web-host=WEB_HOST   Host to bind the web interface to. Defaults to '' (all
 6                         interfaces)
 7   -P PORT, --port=PORT, --web-port=PORT
 8                         Port on which to run web host
 9   -f LOCUSTFILE, --locustfile=LOCUSTFILE
10                         Python module file to import, e.g. '../other.py'.
11                         Default: locustfile
12   --csv=CSVFILEBASE, --csv-base-name=CSVFILEBASE
13                         Store current request stats to files in CSV format.
14   --master              Set locust to run in distributed mode with this
15                         process as master
16   --slave               Set locust to run in distributed mode with this
17                         process as slave
18   --master-host=MASTER_HOST
19                         Host or IP address of locust master for distributed
20                         load testing. Only used when running with --slave.
21                         Defaults to 127.0.0.1.
22   --master-port=MASTER_PORT
23                         The port to connect to that is used by the locust
24                         master for distributed load testing. Only used when
25                         running with --slave. Defaults to 5557. Note that
26                         slaves will also connect to the master node on this
27                         port + 1.
28   --master-bind-host=MASTER_BIND_HOST
29                         Interfaces (hostname, ip) that locust master should
30                         bind to. Only used when running with --master.
31                         Defaults to * (all available interfaces).
32   --master-bind-port=MASTER_BIND_PORT
33                         Port that locust master should bind to. Only used when
34                         running with --master. Defaults to 5557. Note that
35                         Locust will also use this port + 1, so by default the
36                         master node will bind to 5557 and 5558.
37   --heartbeat-liveness=HEARTBEAT_LIVENESS
38                         set number of seconds before failed heartbeat from
39                         slave
40   --heartbeat-interval=HEARTBEAT_INTERVAL
41                         set number of seconds delay between slave heartbeats
42                         to master
43   --expect-slaves=EXPECT_SLAVES
44                         How many slaves master should expect to connect before
45                         starting the test (only when --no-web used).
46   --no-web              Disable the web interface, and instead start running
47                         the test immediately. Requires -c and -r to be
48                         specified.
49   -c NUM_CLIENTS, --clients=NUM_CLIENTS
50                         Number of concurrent Locust users. Only used together
51                         with --no-web
52   -r HATCH_RATE, --hatch-rate=HATCH_RATE
53                         The rate per second in which clients are spawned. Only
54                         used together with --no-web
55   -t RUN_TIME, --run-time=RUN_TIME
56                         Stop after the specified amount of time, e.g. (300s,
57                         20m, 3h, 1h30m, etc.). Only used together with --no-
58                         web
59   -L LOGLEVEL, --loglevel=LOGLEVEL
60                         Choose between DEBUG/INFO/WARNING/ERROR/CRITICAL.
61                         Default is INFO.
62   --logfile=LOGFILE     Path to log file. If not set, log will go to
63                         stdout/stderr
64   --print-stats         Print stats in the console
65   --only-summary        Only print the summary stats
66   --no-reset-stats      [DEPRECATED] Do not reset statistics once hatching has
67                         been completed. This is now the default behavior. See
68                         --reset-stats to disable
69   --reset-stats         Reset statistics once hatching has been completed.
70                         Should be set on both master and slaves when running
71                         in distributed mode
72   -l, --list            Show list of possible locust classes and exit
73   --show-task-ratio     print table of the locust classes' task execution
74                         ratio
75   --show-task-ratio-json
76                         print json data of the locust classes' task execution
77                         ratio
78   -V, --version         show program's version number and exit

参数说明:

  1. -h, --help 查看帮助
  2. -H HOST, --host=HOST 指定被测试的主机,采用以格式:http://10.21.32.33
  3. --web-host=WEB_HOST 指定运行 Locust Web 页面的主机,默认为空 ''
  4. -P PORT, --port=PORT, --web-port=PORT 指定 --web-host 的端口,默认是8089
  5. -f LOCUSTFILE, --locustfile=LOCUSTFILE 指定运行 Locust 性能测试文件,默认为: locustfile.py
  6. --csv=CSVFILEBASE, --csv-base-name=CSVFILEBASE CSV格式存储当前请求测试数据。
  7. --master Locust 分布式模式使用,当前节点为 master 节点。
  8. --slave Locust 分布式模式使用,当前节点为 slave 节点。
  9. --master-host=MASTER_HOST 分布式模式运行,设置 master 节点的主机或 IP 地址,只在与 --slave 节点一块儿运行时使用,默认为:127.0.0.1.
  10. --master-port=MASTER_PORT 分布式模式运行, 设置 master 节点的端口号,只在与 --slave 节点一块儿运行时使用,默认为:5557。注意,slave 节点也将链接到这个端口+1 上的 master 节点。
  11. --master-bind-host=MASTER_BIND_HOST Interfaces (hostname, ip) that locust master should bind to. Only used when running with --master. Defaults to * (all available interfaces).
  12. --master-bind-port=MASTER_BIND_PORT Port that locust master should bind to. Only used when running with --master. Defaults to 5557. Note that Locust will also use this port + 1, so by default the master node will bind to 5557 and 5558.
  13. --expect-slaves=EXPECT_SLAVES How many slaves master should expect to connect before starting the test (only when --no-web used).
  14. --no-web no-web 模式运行测试,须要 -c -r 配合使用.
  15. -c NUM_CLIENTS, --clients=NUM_CLIENTS 指定并发用户数,做用于 --no-web 模式。
  16. -r HATCH_RATE, --hatch-rate=HATCH_RATE 指定每秒启动的用户数,做用于 --no-web 模式。
  17. -t RUN_TIME, --run-time=RUN_TIME 设置运行时间, 例如: (300s, 20m, 3h, 1h30m). 做用于 --no-web 模式。
  18. -L LOGLEVEL, --loglevel=LOGLEVEL 选择 log 级别(DEBUG/INFO/WARNING/ERROR/CRITICAL). 默认是 INFO.
  19. --logfile=LOGFILE 日志文件路径。若是没有设置,日志将去 stdout/stderr
  20. --print-stats 在控制台中打印数据
  21. --only-summary 只打印摘要统计
  22. --no-reset-stats Do not reset statistics once hatching has been completed
  23. -l, --list 显示测试类, 配置 -f 参数使用
  24. --show-task-ratio 打印 locust 测试类的任务执行比例,配合 -f 参数使用.
  25. --show-task-ratio-json json 格式打印 locust 测试类的任务执行比例,配合 -f 参数使用.
  26. -V, --version 查看当前 Locust 工具的版本.

四、Locust主要由下面的几个库构成:

1) gevent

gevent是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。

2) flask

Python编写的轻量级Web应用框架。

3) requests

Python Http库

4) msgpack-python

MessagePack是一种快速、紧凑的二进制序列化格式,适用于相似JSON的数据格式。msgpack-python主要提供MessagePack数据序列化及反序列化的方法。

5) six

Python2和3兼容库,用来封装Python2和Python3之间的差别性

6) pyzmq

pyzmq是zeromq(一种通讯队列)的Python绑定,主要用来实现Locust的分布式模式运行

当咱们在安装 Locust 时,它会检测咱们当前的 Python 环境是否已经安装了这些库,若是没有安装,它会先把这些库一一装上。而且对这些库版本有要求,有些是必须等于某版本,有些是大于某版本。咱们也能够事先把这些库所有按要求装好,再安装Locust时就会快上许多。

3、编写接口压测脚本文件locustfile.py

脚本模板(参考)

 1 from locust import HttpLocust, TaskSet, task
 2 
 3 class UserBehavior(TaskSet):
 4     def setup(self):
 5         print('task setup')
 6 
 7     def teardown(self):
 8         print('task teardown')
 9 
10     def on_start(self):
11         # 虚拟用户启动Task时运行
12         print('start')
13 
14     def on_stop(self):
15         # 虚拟用户结束Task时运行
16         print('end')
17 
18     @task(2)
19     def index(self):
20         self.client.get("/")
21 
22     @task(1)
23     def profile(self):
24         self.client.get("/profile")
25 
26 class WebsiteUser(HttpLocust):
27     def setup(self):
28         print('locust setup')
29 
30     def teardown(self):
31         print('locust teardown')
32 
33     host = http: // XXXXX.com
34     task_set = UserBehavior
35     min_wait = 5000
36     max_wait = 9000
37 
38 if __name__ == '__main__':
39     pass

说明:

Locust类有setup和teardown,TaskSet类有setup、teardown、on_start、on_stop。

每次启动locust时运行setup方法,退出时运行teardown方法,locust执行TaskSet时运行TaskSet的setup方法,退出时运行teardown方法,每一个虚拟用户执行操做时运行on_start方法,退出时执行on_stop方法,运行上面的脚本,执行顺序以下:

执行顺序:Locust setup → TaskSet setup → TaskSet on_start → TaskSet tasks → TaskSet on_stop → TaskSet teardown → Locust teardown

举个脚本栗子

 1 from locust import HttpLocust, TaskSet, task
 2 
 3 
 4 class ScriptTasks(TaskSet):
 5     def on_start(self):
 6         self.client.post("/login", {
 7             "username": "test",
 8             "password": "123456"
 9         })
10 
11     @task(2)
12     def index(self):
13         self.client.get("/")
14 
15     @task(1)
16     def about(self):
17         self.client.get("/about/")
18 
19     @task(1)
20     def demo(self):
21         payload = {}
22         headers = {}
23         self.client.post("/demo/", data=payload, headers=headers)
24 
25 
26 class WebsiteUser(HttpLocust):
27     task_set = ScriptTasks
28     host = "http://example.com"
29     min_wait = 1000
30     max_wait = 5000

脚本解读

  1. 建立ScriptTasks()类继承TaskSet类: 用户行为类,用于定义测试业务场景。
  2. 建立index()、about()、demo()方法分别表示一个行为,访问http://example.com。用@task() 装饰该方法为一个任务。一、2表示一个Locust实例被挑选执行的权重,数值越大,执行频率越高。在当前ScriptTasks()行为下的三个方法得执行比例为2:1:1
  3. WebsiteUser()类: 用于定义模拟用户。
  4. task_set 指向一个定义了的用户行为类。
  5. host 指定被测试应用的URL的地址
  6. min_wait 用户执行任务之间等待时间的下界,单位:毫秒。
  7. max_wait 用户执行任务之间等待时间的上界,单位:毫秒。

脚本使用场景解读

一、在这个示例中,定义了针对http://example.com网站的测试场景:先模拟用户登陆系统,而后随机地访问首页(/)和关于页面(/about/),请求比例为2:1,demo方法主要用来阐述client对post接口的处理方式;而且,在测试过程当中,两次请求的间隔时间为1->5秒间的随机值。

二、从脚本中能够看出,脚本主要包含两个类(类名可自定义),一个是WebsiteUser(继承自HttpLocust,而HttpLocust继承自Locust),另外一个是ScriptTasks(继承自TaskSet)。事实上,在Locust的测试脚本中,全部业务测试场景都是在LocustTaskSet两个类的继承子类中进行描的。

三、那如何理解LocustTaskSet这两个类呢?简单地说,Locust类就比如是一群蝗虫,而每一只蝗虫就是一个类的实例。相应的,TaskSet类就比如是蝗虫的大脑,控制着蝗虫的具体行为,即实际业务场景测试对应的任务集。

4、Locust类

实例脚本

伪代码:

 1 from locust import HttpLocust, TaskSet, task
 2 
 3 
 4 class WebsiteTasks(TaskSet):
 5     def on_start(self):  # 进行初始化的工做,每一个Locust用户开始作的第一件事
 6         payload = {
 7             "username": "test_user",
 8             "password": "123456",
 9         }
10         header = {
11             "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
12         }
13         self.client.post("/login", data=payload,headers=header)  
14         # self.client属性使用Python request库的全部方法,调用和使用方法和requests彻底一致;
15 
16     @task(5)  
17     # 经过@task()装饰的方法为一个事务,方法的参数用于指定该行为的执行权重,参数越大每次被虚拟用户执行的几率越高,默认为1
18     def index(self):
19         self.client.get("/")
20 
21     @task(1)
22     def about(self):
23         self.client.get("/about/")
24 
25 
26 class WebsiteUser(HttpLocust):
27     host = "https://github.com/"  # 被测系统的host,在终端中启动locust时没有指定--host参数时才会用到
28     task_set = WebsiteTasks  # TaskSet类,该类定义用户任务信息,必填。这里就是:WebsiteTasks类名,由于该类继承TaskSet;
29     min_wait = 5000  # 每一个用户执行两个任务间隔时间的上下限(毫秒),具体数值在上下限中随机取值,若不指定默认间隔时间固定为1秒
30     max_wait = 15000

 伪代码中对https://github.com/网站的测试场景,先模拟用户登陆系统,而后随机访问首页/和/about/,请求比例5:1,而且在测试过程当中,两次请求的间隔时间1-5秒的随机值;

 on_start方法,在正式执行测试前执行一次,主要用于完成一些初始化的工做,例如登陆操做;

WebsiteTasks类中如何去调用 WebsiteUser(HttpLocust)类中定义的字段和方法呢?

 经过在WebsiteTasks类中self.locust.xxoo      xxoo就是咱们在WebsiteUser类中定义的字段或方法;

伪代码:

 1 from locust import HttpLocust, TaskSet, task
 2 import hashlib
 3 import Queue
 4 
 5 
 6 class WebsiteTasks(TaskSet):
 7     @task(5)
 8     def index(self):
 9         data = self.locust.user_data_queue  # 获取WebsiteUser里面定义的ser_data_queue队列
10         md5_data = self.locust.md5_encryption()  # 获取WebsiteUser里面定义的md5_encryption()方法
11         self.client.get("/")
12 
13 
14 class WebsiteUser(HttpLocust):
15     host = "https://github.com/"
16     task_set = WebsiteTasks
17     min_wait = 5000
18     max_wait = 15000
19     user_data_queue = Queue.Queue()
20 
21     def md5_encryption(self, star):
22         '''md5加密方法'''
23         obj = hashlib.md5()
24         obj.update(bytes(star, encoding="utf-8"))
25         result = obj.hexdigest()
26         return result

伪代码中测试场景如何表达?

代码主要包含两个类:

  1. WebsiteUser继承(HttpLocust,而HttpLocust继承自Locust)
  2. WebsiteTasks继承(TaskSet)

在Locust测试脚本中,全部业务测试场景都是在Locust和TaskSet两个类的继承子类中进行描述;

简单说:Locust类就相似一群蝗虫,而每只蝗虫就是一个类的实例。TaskSet类就相似蝗虫的大脑,控制蝗虫的具体行为,即实际业务场景测试对应的任务集;

源码中:class Locust(object)和class HttpLocust(Locust) 此处可查看源代码

在Locust类中,静态字段client即客户端的请求方法,这里的client字段没有绑定客户端请求方法,所以在使用Locust时,须要先继承Locust类class HttpLocust(Locust),而后在self.client =HttpSession(base_url=self.host)绑定客户端请求方法;

对于常见的HTTP(s)协议,Locust已经实现了HttpLocust类,其self.client=HttpSession(base_url=self.host),而HttpSession继承自requests.Session。所以在测试HTTP(s)的Locust脚本中,能够经过client属性来使用Python requests库的所 有方法,调用方式与      reqeusts彻底一致。另外,因为requests.Session的使用,client的方法调用之间就自动具备了状态记忆功能。常见的场景就是,在登陆系统后能够维持登陆状态的Session,从然后续HTTP请求操做都能带上登陆状态;

Locust类中,除了client属性,还有几个属性须要关注:

  • task_set ---> 指向一个TaskSet类,TaskSet类定义了用户的任务信息,该静态字段为必填;
  • max_wait/min_wait ---> 每一个用户执行两个任务间隔的上下限(毫秒),具体数值在上下限中随机取值,若不指定则默认间隔时间为1秒;
  • host    --->被测试系统的host,当在终端中启动locust时没有指定--host参数时才会用到;
  • weight--->同时运行多个Locust类时,用于控制不一样类型的任务执行权重;

Locust流程,测试开始后,每一个虚拟用户(Locust实例)运行逻辑都会遵照以下规律:

  1. 先执行WebsiteTasks中的on_start(只执行一次),做为初始化;
  2. 从WebsiteTasks中随机挑选(若是定义了任务间的权重关系,那么就按照权重关系随机挑选)一个任务执行;
  3. 根据Locust类中min_wait和max_wait定义的间隔时间范围(若是TaskSet类中也定义了min_wait或者max_wait,以TaskSet中的优先),在时间范围中随机取一个值,休眠等待;
  4. 重复2~3步骤,直到测试任务终止;

class TaskSet

TaskSet类实现了虚拟用户所执行任务的调度算法,包括规划任务执行顺序(schedule_task)、挑选下一个任务(execute_next_task)、执行任务(execute_task)、休眠等待(wait)、中断控制(interrupt)等待。在此基础上,就能够在TaskSet子类中采用很是简洁的方式来描述虚拟用户的业务测试场景,对虚拟用户的全部行为进行组织和描述,并能够对不一样任务的权重进行配置。

@task

经过@task()装饰的方法为一个事务。方法的参数用于指定该行为的执行权重。参数越大每次被虚拟用户执行的几率越高。若是不设置默认为1。

TaskSet子类中定义任务信息时,采起两种方式:@task装饰器和tasks属性。

采用@task装饰器定义任务信息时:

 1 from locust import TaskSet, task
 2 
 3 class UserBehavior(TaskSet):
 4     @task(1)
 5     def test_job1(self):
 6         self.client.get('/test1')
 7 
 8     @task(3)
 9     def test_job2(self):
10         self.client.get('/test2')

 

采用tasks属性定义任务信息时

 1 from locust import TaskSet
 2 
 3 def test_job1(obj):
 4     obj.client.get('/test1')
 5 
 6 def test_job2(obj):
 7     obj.client.get('/test2')
 8 
 9 class UserBehavior(TaskSet):
10     tasks = {test_job1: 1, test_job2: 2}
11     # tasks = [(test_job1,1), (test_job1,3)] # 两种方式等价

上面两种定义任务信息方式中,均设置了权重属性,即执行test_job2的频率是test_job1的两倍。若不指定,默认比例为1:1。

高级用法:

关联

在某些请求中,须要携带以前response中提取的参数,常见场景就是session_id。Python中可用经过re正则匹配,对于返回的html页面,可用采用lxml库来定位获取须要的参数;

 1 from locust import HttpLocust, TaskSet, task
 2 from lxml import etree
 3 
 4 class WebsiteTasks(TaskSet):
 5     def get_session(self, html):  # 关联例子
 6         tages = etree.HTML(html)
 7         return tages.xpath("//div[@class='btnbox']/input[@name='session']/@value")[0]
 8 
 9     def on_start(self):
10         html = self.client.get('/index')
11         session = self.get_session(html.text)
12         payload = {
13             "username": "test_user",
14             "password": "123456",
15             'session': session
16         }
17         header = {
18             "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"19         }
20         self.client.post("/login", data=payload, headers=header)
21 
22     @task(5)
23     def index(self):
24         self.client.get("/")
25         assert response['ErrorCode'] == 0  # 断言
26 
27     @task(1)
28     def about(self):
29         self.client.get("/about/")
30 
31 class WebsiteUser(HttpLocust):
32     host = "https://github.com/"
33     task_set = WebsiteTasks
34     min_wait = 5000
35     max_wait = 15000

参数化

做用:循环取数据,数据可重复使用

 例如:模拟3个用户并发请求网页,共有100个URL地址,每一个虚拟用户都会依次循环加载100个URL地址

 1 from locust import TaskSet, task, HttpLocust
 2 
 3 class UserBehavior(TaskSet):
 4     def on_start(self):
 5         self.index = 0
 6 
 7     @task
 8     def test_visit(self):
 9         url = self.locust.share_data[self.index]
10         print('visit url: %s' % url)
11         self.index = (self.index + 1) % len(self.locust.share_data)
12         self.client.get(url)
13 
14 class WebsiteUser(HttpLocust):
15     host = 'http://debugtalk.com'
16     task_set = UserBehavior
17     share_data = ['url1', 'url2', 'url3', 'url4', 'url5']
18     min_wait = 1000
19     max_wait = 3000

 保证并发测试数据惟一性,不循环取数据;

 全部并发虚拟用户共享同一份测试数据,而且保证虚拟用户使用的数据不重复;

例如:模拟3用户并发注册帐号,共有9个帐号,要求注册帐号不重复,注册完毕后结束测试:

采用队列

 1 from locust import TaskSet, task, HttpLocust
 2 import Queue
 3 
 4 class UserBehavior(TaskSet):
 5     @task
 6     def test_register(self):
 7         try:
 8             data = self.locust.user_data_queue.get()
 9         except Queue.Empty:
10             print('account data run out, test ended.')
11             exit(0)
12         print('register with user: {}, pwd: {}'.format(data['username'], data['password']))
13         payload = {
14             'username': data['username'],
15             'password': data['password']
16         }
17         self.client.post('/register', data=payload)
18 
19 class WebsiteUser(HttpLocust):
20     host = 'http://XXXXX.com'
21     task_set = UserBehavior
22     user_data_queue = Queue.Queue()
23     for index in range(100):
24         data = {
25             "username": "test%04d" % index,
26             "password": "pwd%04d" % index,
27             "email": "test%04d@debugtalk.test" % index,
28             "phone": "186%08d" % index,
29         }
30         user_data_queue.put_nowait(data)
31     min_wait = 1000
32     max_wait = 3000

保证并发测试数据惟一性,循环取数据;

全部并发虚拟用户共享同一份测试数据,保证并发虚拟用户使用的数据不重复,而且数据可循环重复使用;

例如:模拟3个用户并发登陆帐号,总共有9个帐号,要求并发登陆帐号不相同,但数据可循环使用;

 

 1 class UserBehavior(TaskSet):
 2     @task
 3     def test_register(self):
 4         try:
 5             data = self.locust.user_data_queue.get()
 6         except Queue.Empty:
 7             print('account data run out, test ended')
 8             exit(0)
 9         print('register with user: {0}, pwd: {1}'.format(data['username'], data['password']))
10         payload = {
11             'username': data['username'],
12             'password': data['password']
13         }
14         self.client.post('/register', data=payload)
15         self.locust.user_data_queue.put_nowait(data)
16 
17 class WebsiteUser(HttpLocust):
18     host = 'http://XXXXXX.com'
19     task_set = UserBehavior
20     user_data_queue = Queue.Queue()
21     for index in range(100):
22         data = {
23             "username": "test%04d" % index,
24             "password": "pwd%04d" % index,
25             "email": "test%04d@debugtalk.test" % index,
26             "phone": "186%08d" % index,
27         }
28         user_data_queue.put_nowait(data)
29     min_wait = 1000
30     max_wait = 3000

断言(即检查点)

性能测试也须要设置断言么? 某些状况下是须要,好比你在请求一个页面时,就能够经过状态来判断返回的 HTTP 状态码是否是 200。

经过with self.client.get("url地址",catch_response=True) as response的形式;

response.status_code获取http响应码进行判断,失败后会加到统计错误表中;

python自带的断言assert失败后代码就不会向下走,且失败后不会被Locust报表统计进去;

默认不写参数catch_response=False断言无效,将catch_response=True才生效;

下面例子中:

首先使用python断言对接口返回值进行判断(python断言不经过,代码就不向下执行,get请求数为0),经过后对该接口的http响应是否为200进行判断;

 1 @task
 2 def all_interface(self):
 3     # 豆瓣图书api为例子
 4     with  self.client.get("https://api.douban.com/v2/book/1220562", name="/LhcActivity/GetActConfig",
 5                           catch_response=True) as response:
 6         assert response.json()['rating']['max'] == 10  # python断言对接口返回值中的max字段进行断言
 7         if response.status_code == 200:  # 对http响应码是否200进行判断
 8             response.success()
 9         else:
10             response.failure("GetActConfig[Failed!]")

5、Locust运行模式

运行Locust时,一般会使用到两种运行模式:单进程运行多进程分布式运行

单进程运行模式

Locust全部的虚拟并发用户均运行在单个Python进程中,具体从使用形式上,又分为no_webweb两种形式。该种模式因为单进程的缘由,并不能彻底发挥压力机全部处理器的能力,所以主要用于调试脚本和小并发压测的状况。

当并发压力要求较高时,就须要用到Locust的多进程分布式运行模式。从字面意思上看,你们可能第一反应就是多台压力机同时运行,每台压力机分担负载一部分的压力生成。的确,Locust支持任意多台压力机(一主多从)的分布式运行模式,但这里说到的多进程分布式运行模式还有另一种状况,就是在同一台压力机上开启多个slave的状况。这是由于当前阶段大多数计算机的CPU都是多处理器(multiple processor cores),单进程运行模式下只能用到一个处理器的能力,而经过在一台压力机上运行多个slave,就能调用多个处理器的能力了。比较好的作法是,若是一台压力机有N个处理器内核,那么就在这台压力机上启动一个masterNslave。固然,咱们也能够启动N的倍数个slave,可是根据个人试验数据,效果跟N个差很少,所以只须要启动Nslave便可。 

no_web形式启动locust:

若是采用no_web形式,则需使用--no-web参数,并会用到以下几个参数。

  • -c, --clients:指定并发用户数;
  • -r, --hatch-rate:指定并发加压速率,默认值位1。

示例:

$ locust -f    locustfile.py     --host = xxxxx.com   --no-web -c 2 -r 1

在此基础上,当咱们想要调试Locust脚本时,就能够在脚本中须要调试的地方经过print打印日志,而后将并发数和总执行次数都指定为1

$ locust -f    locustfile.py     --host = xxxxx.com   --no-web -c 1 -r 1

执行测试

经过这种方式,咱们就能很方便地对Locust脚本进行调试了。

Locust脚本调试经过后,就算是完成了全部准备工做,能够开始进行压力测试了。

web形式启动locust:

若是采用web形式,,则一般状况下无需指定其它额外参数,Locust默认采用8089端口启动web;若是要使用其它端口,就可使用以下参数进行指定。

  • -P, --port:指定web端口,默认为8089.
  •  终端中--->进入到代码目录: locust     -f    locustfile.py     --host = xxxxx.com      
  • -f            指定性能测试脚本文件
  • -host      被测试应用的URL地址【若是不填写,读取继承(HttpLocust)类中定义的host】
  • 若是Locust运行在本机,在浏览器中访问http://localhost:8089便可进入Locust的Web管理页面;若是Locust运行在其它机器上,那么在浏览器中访问http://locust_machine_ip:8089便可。

多进程分布式运行

无论是单机多进程,仍是多机负载模式,运行方式都是同样的,都是先运行一个master,再启动多个slave

启动master时,须要使用--master参数;一样的,若是要使用8089之外的端口,还须要使用-P, --port参数。

  1. D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --master --port=8089
  2. [2018-06-05 15:36:30,654] dengshihuang/INFO/locust.main: Starting web monitor at *:8089
  3. [2018-06-05 15:36:30,684] dengshihuang/INFO/locust.main: Starting Locust 0.8.1

启动slave时须要使用--slave参数;在slave中,就不须要再指定端口了。master启动后,还须要启动slave才能执行测试任务。

  1. D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --slave
  2. [2018-06-05 15:36:30,654] dengshihuang/INFO/locust.main: Starting web monitor at *:8089
  3. [2018-06-05 15:36:30,684] dengshihuang/INFO/locust.main: Starting Locust 0.8.1
  1. D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --slave --master-host=<locust_machine_ip>

masterslave都启动完毕后,就能够在浏览器中经过http://locust_machine_ip:8089进入Locust的Web管理页面了。使用方式跟单进程web形式彻底相同,只是此时是经过多进程负载来生成并发压力,在web管理界面中也能看到实际的slave数量。若是slavemaster不在同一台机器上,还须要经过--master-host参数再指定master的IP地址。

运行结果:

 Number of users to simulate    设置虚拟用户数,对应中no_web模式的-c, --clients参数;

 Hatch rate(users spawned/second)每秒产生(启动)的虚拟用户数 , 对应着no_web模式的-r, --hatch-rate参数,默认为1。点击Start swarming 按钮,开始运行性能测试。

上图:启动了一个 master 和两个 slave,由两个 slave 来向被测试系统发送请求

性能测试参数

  • Type: 请求的类型,例如GET/POST。

  • Name:请求的路径。这里为百度首页,即:https://www.baidu.com/

  • request:当前请求的数量。

  • fails:当前请求失败的数量。

  • Median:中间值,单位毫秒,一半的服务器响应时间低于该值,而另外一半高于该值。

  • Average:平均值,单位毫秒,全部请求的平均响应时间。

  • Min:请求的最小服务器响应时间,单位毫秒。

  • Max:请求的最大服务器响应时间,单位毫秒。

  • Content Size:单个请求的大小,单位字节。

  • reqs/sec:是每秒钟请求的个数。

 相比于LoadRunnerLocust的结果展现十分简单,主要就四个指标:并发数RPS响应时间异常率。但对于大多数场景来讲,这几个指标已经足够了。

在上图中,RPS平均响应时间这两个指标显示的值都是根据最近2秒请求响应数据计算获得的统计值,咱们也能够理解为瞬时值。

若是想看性能指标数据的走势,就能够在Charts栏查看。在这里,能够查看到RPS平均响应时间在整个运行过程当中的波动状况。

除了以上数据,Locust还提供了整个运行过程数据的百分比统计值,例如咱们经常使用的90%响应时间响应时间中位值;平均响应时间和错误数的统计,该数据能够经过Download response time distribution CSV和Download request statistics CSV得到,数据展现效果以下所示。

 

-----------------------------------------------------------

注意:

locust虽然使用方便,可是加压性能和响应时间上面仍是有差距的,若是项目有很是大的并发加压请求,能够选择wrk

对比方法与结果:

能够准备两台服务器,服务器A做为施压方,服务器B做为承压方
服务器B上简单的运行一个nginx服务就好了

服务器A上能够安装一些经常使用的压测工具,好比locust、ab、wrk

实测下来,施压能力上 wrk > golang >> ab > locust

由于locust一个进程只使用一核CPU,因此用locust压测时,必须使用主从分布式(zeromq通信)模式,并根据服务器CPU核数来起slave节点数

wrk约为55K QPSgolang net/http 约 45K QPSab 大约 15K QPSlocust 最差,并且response time明显比较长

相关文章
相关标签/搜索