一、写在前面

官网: https://www.locust.io/
官方使用文档:https://docs.locust.io/en/latest/
大并发量测试时,建议在linux系统下进行。
二、Locust安装
1.1、 ---> pip3 install locust
1.2 、 通过GitHub上克隆项目安装(Python3推荐):https://github.com/locustio/locust ,然后执行 ...\locust> python setup.py install
2、安装 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
3、安装成功,CMD敲入命令验证。 /> locust --help
- Options:
- -h, --help show this help message and exit
- -H HOST, --host=HOST Host to load test in the following format:
- http://10.21.32.33
- --web-host=WEB_HOST Host to bind the web interface to. Defaults to '' (all
- interfaces)
- -P PORT, --port=PORT, --web-port=PORT
- Port on which to run web host
- -f LOCUSTFILE, --locustfile=LOCUSTFILE
- Python module file to import, e.g. '../other.py'.
- Default: locustfile
- --master Set locust to run in distributed mode with this
- process as master
- --slave Set locust to run in distributed mode with this
- process as slave
- --master-host=MASTER_HOST
- Host or IP address of locust master for distributed
- load testing. Only used when running with --slave.
- Defaults to 127.0.0.1.
- --master-port=MASTER_PORT
- The port to connect to that is used by the locust
- master for distributed load testing. Only used when
- running with --slave. Defaults to 5557. Note that
- slaves will also connect to the master node on this
- port + 1.
- --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).
- --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.
- --no-web Disable the web interface, and instead start running
- the test immediately. Requires -c and -r to be
- specified.
- -c NUM_CLIENTS, --clients=NUM_CLIENTS
- Number of concurrent clients. Only used together with
- --no-web
- -r HATCH_RATE, --hatch-rate=HATCH_RATE
- The rate per second in which clients are spawned. Only
- used together with --no-web
- -n NUM_REQUESTS, --num-request=NUM_REQUESTS
- Number of requests to perform. Only used together with
- --no-web
- -L LOGLEVEL, --loglevel=LOGLEVEL
- Choose between DEBUG/INFO/WARNING/ERROR/CRITICAL.
- Default is INFO.
- --logfile=LOGFILE Path to log file. If not set, log will go to
- stdout/stderr
- --print-stats Print stats in the console
- --only-summary Only print the summary stats
- -l, --list Show list of possible locust classes and exit
- --show-task-ratio print table of the locust classes' task execution
- ratio
- --show-task-ratio-json
- print json data of the locust classes' task execution
- ratio
- -V, --version show program's version number and exit
参数说明:
- -h, --help 查看帮助
- -H HOST, --host=HOST 指定被测试的主机,采用以格式:http://10.21.32.33
- --web-host=WEB_HOST 指定运行 Locust Web 页面的主机,默认为空 ''。
- -P PORT, --port=PORT, --web-port=PORT 指定 --web-host 的端口,默认是8089
- -f LOCUSTFILE, --locustfile=LOCUSTFILE 指定运行 Locust 性能测试文件,默认为: locustfile.py
- --csv=CSVFILEBASE, --csv-base-name=CSVFILEBASE 以CSV格式存储当前请求测试数据。
- --master Locust 分布式模式使用,当前节点为 master 节点。
- --slave Locust 分布式模式使用,当前节点为 slave 节点。
- --master-host=MASTER_HOST 分布式模式运行,设置 master 节点的主机或 IP 地址,只在与 --slave 节点一起运行时使用,默认为:127.0.0.1.
- --master-port=MASTER_PORT 分布式模式运行, 设置 master 节点的端口号,只在与 --slave 节点一起运行时使用,默认为:5557。注意,slave 节点也将连接到这个端口+1 上的 master 节点。
- --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).
- --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.
- --expect-slaves=EXPECT_SLAVES How many slaves master should expect to connect before starting the test (only when --no-web used).
- --no-web no-web 模式运行测试,需要 -c 和 -r 配合使用.
- -c NUM_CLIENTS, --clients=NUM_CLIENTS 指定并发用户数,作用于 --no-web 模式。
- -r HATCH_RATE, --hatch-rate=HATCH_RATE 指定每秒启动的用户数,作用于 --no-web 模式。
- -t RUN_TIME, --run-time=RUN_TIME 设置运行时间, 例如: (300s, 20m, 3h, 1h30m). 作用于 --no-web 模式。
- -L LOGLEVEL, --loglevel=LOGLEVEL 选择 log 级别(DEBUG/INFO/WARNING/ERROR/CRITICAL). 默认是 INFO.
- --logfile=LOGFILE 日志文件路径。如果没有设置,日志将去 stdout/stderr
- --print-stats 在控制台中打印数据
- --only-summary 只打印摘要统计
- --no-reset-stats Do not reset statistics once hatching has been completed。
- -l, --list 显示测试类, 配置 -f 参数使用
- --show-task-ratio 打印 locust 测试类的任务执行比例,配合 -f 参数使用.
- --show-task-ratio-json 以 json 格式打印 locust 测试类的任务执行比例,配合 -f 参数使用.
- -V, --version 查看当前 Locust 工具的版本.
4、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时就会快上许多。
三、编写接口压测脚本文件locustfile.py
- 1 from locust import HttpLocust, TaskSet, task
- 2
- 3 class ScriptTasks(TaskSet):
- 4 def on_start(self):
- 5 self.client.post("/login", {
- 6 "username": "test",
- 7 "password": "123456"
- 8 })
- 9
- 10 @task(2)
- 11 def index(self):
- 12 self.client.get("/")
- 13
- 14 @task(1)
- 15 def about(self):
- 16 self.client.get("/about/")
- 17
- 18 @task(1)
- 19 def demo(self):
- 20 payload={}
- 21 headers={}
- 22 self.client.post("/demo/",data=payload, headers=headers)
- 23
- 24 class WebsiteUser(HttpLocust):
- 25 task_set = ScriptTasks
- 26 host = "http://example.com"
- 27 min_wait = 1000
- 28 max_wait = 5000
脚本解读:
- 1、创建ScriptTasks()类继承TaskSet类: 用于定义测试业务。
- 2、创建index()、about()、demo()方法分别表示一个行为,访问http://example.com。用@task() 装饰该方法为一个任务。1、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
的测试脚本中,所有业务测试场景都是在Locust
和TaskSet
两个类的继承子类中进行描的。
那如何理解Locust
和TaskSet
这两个类呢?简单地说,Locust类
就好比是一群蝗虫,而每一只蝗虫就是一个类的实例。相应的,TaskSet类
就好比是蝗虫的大脑,控制着蝗虫的具体行为,即实际业务场景测试对应的任务集。
四、Locust类
实例脚本
伪代码:
- from locust import HttpLocust, TaskSet, task
- class WebsiteTasks(TaskSet):
- def on_start(self): #进行初始化的工作,每个Locust用户开始做的第一件事
- payload = {
- "username": "test_user",
- "password": "123456",
- }
- header = {
- "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
- }
- self.client.post("/login",data=payload,headers=header)#self.client属性使用Python request库的所有方法,调用和使用方法和requests完全一致;
- @task(5) #通过@task()装饰的方法为一个事务,方法的参数用于指定该行为的执行权重,参数越大每次被虚拟用户执行的概率越高,默认为1
- def index(self):
- self.client.get("/")
- @task(1)
- def about(self):
- self.client.get("/about/")
- class WebsiteUser(HttpLocust):
- host = "https://github.com/" #被测系统的host,在终端中启动locust时没有指定--host参数时才会用到
- task_set = WebsiteTasks #TaskSet类,该类定义用户任务信息,必填。这里就是:WebsiteTasks类名,因为该类继承TaskSet;
- min_wait = 5000 #每个用户执行两个任务间隔时间的上下限(毫秒),具体数值在上下限中随机取值,若不指定默认间隔时间固定为1秒
- max_wait = 15000
伪代码中对https://github.com/网站的测试场景,先模拟用户登录系统,然后随机访问首页/和/about/,请求比例5:1,并且在测试过程中,两次请求的间隔时间1-5秒的随机值;
on_start方法,在正式执行测试前执行一次,主要用于完成一些初始化的工作,例如登录操作;
WebsiteTasks类中如何去调用 WebsiteUser(HttpLocust)类中定义的字段和方法呢?
通过在WebsiteTasks类中self.locust.xxoo xxoo就是我们在WebsiteUser类中定义的字段或方法;
伪代码:
- from locust import HttpLocust, TaskSet, task
- import hashlib
- import queue
-
- class WebsiteTasks(TaskSet):
-
- @task(5)
- def index(self):
- data = self.locust.user_data_queue #获取WebsiteUser里面定义的ser_data_queue队列
- md5_data=self.locust.md5_encryption() #获取WebsiteUser里面定义的md5_encryption()方法
- self.client.get("/")
-
- class WebsiteUser(HttpLocust):
- host = "https://github.com/"
- task_set = WebsiteTasks
- min_wait = 5000
- max_wait = 15000
- user_data_queue = queue.Queue()
-
- def md5_encryption(self,star):
- '''md5加密方法'''
- obj = hashlib.md5()
- obj.update(bytes(star,encoding="utf-8"))
- result = obj.hexdigest()
- return result
伪代码中测试场景如何表达?
代码主要包含两个类:
- WebsiteUser继承(HttpLocust,而HttpLocust继承自Locust)
- WebsiteTasks继承(TaskSet)
在Locust测试脚本中,所有业务测试场景都是在Locust和TaskSet两个类的继承子类中进行描述;
简单说:Locust类就类似一群蝗虫,而每只蝗虫就是一个类的实例。TaskSet类就类似蝗虫的大脑,控制蝗虫的具体行为,即实际业务场景测试对应的任务集;
源码中:class Locust(object)和class HttpLocust(Locust)
- 1 class Locust(object):
- 2 """
- 3 Represents a "user" which is to be hatched and attack the system that is to be load tested.
- 4
- 5 The behaviour of this user is defined by the task_set attribute, which should point to a
- 6 :py:class:`TaskSet <locust.core.TaskSet>` class.
- 7
- 8 This class should usually be subclassed by a class that defines some kind of client. For
- 9 example when load testing an HTTP system, you probably want to use the
- 10 :py:class:`HttpLocust <locust.core.HttpLocust>` class.
- 11 """
- 12
- 13 host = None
- 14 """Base hostname to swarm. i.e: http://127.0.0.1:1234"""
- 15
- 16 min_wait = 1000
- 17 """Minimum waiting time between the execution of locust tasks"""
- 18
- 19 max_wait = 1000
- 20 """Maximum waiting time between the execution of locust tasks"""
- 21
- 22 task_set = None
- 23 """TaskSet class that defines the execution behaviour of this locust"""
- 24
- 25 stop_timeout = None
- 26 """Number of seconds after which the Locust will die. If None it won't timeout."""
- 27
- 28 weight = 10
- 29 """Probability of locust being chosen. The higher the weight, the greater is the chance of it being chosen."""
- 30
- 31 client = NoClientWarningRaiser()
- 32 _catch_exceptions = True
- 33
- 34 def __init__(self):
- 35 super(Locust, self).__init__()
- 36
- 37 def run(self):
- 38 try:
- 39 self.task_set(self).run()
- 40 except StopLocust:
- 41 pass
- 42 except (RescheduleTask, RescheduleTaskImmediately) as e:
- 43
- 44 class HttpLocust(Locust):
- 45 """
- 46 Represents an HTTP "user" which is to be hatched and attack the system that is to be load tested.
- 47
- 48 The behaviour of this user is defined by the task_set attribute, which should point to a
- 49 :py:class:`TaskSet <locust.core.TaskSet>` class.
- 50
- 51 This class creates a *client* attribute on instantiation which is an HTTP client with support
- 52 for keeping a user session between requests.
- 53 """
- 54
- 55 client = None
- 56 """
- 57 Instance of HttpSession that is created upon instantiation of Locust.
- 58 The client support cookies, and therefore keeps the session between HTTP requests.
- 59 """
- 60 def __init__(self):
- 61 super(HttpLocust, self).__init__()
- 62 if self.host is None:
- 63 raise LocustError("You must specify the base host. Either in the host attribute in the Locust class, or on the command line using the --host option.")
- 64 self.client = HttpSession(base_url=self.host)
在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实例)运行逻辑都会遵守如下规律:
- 先执行WebsiteTasks中的on_start(只执行一次),作为初始化;
- 从WebsiteTasks中随机挑选(如果定义了任务间的权重关系,那么就按照权重关系随机挑选)一个任务执行;
- 根据Locust类中min_wait和max_wait定义的间隔时间范围(如果TaskSet类中也定义了min_wait或者max_wait,以TaskSet中的优先),在时间范围中随机取一个值,休眠等待;
- 重复2~3步骤,直到测试任务终止;
class TaskSet
TaskSet类实现了虚拟用户所执行任务的调度算法,包括规划任务执行顺序(schedule_task)、挑选下一个任务(execute_next_task)、执行任务(execute_task)、休眠等待(wait)、中断控制(interrupt)等待。在此基础上,就可以在TaskSet子类中采用非常简洁的方式来描述虚拟用户的业务测试场景,对虚拟用户的所有行为进行组织和描述,并可以对不同任务的权重进行配置。
@task
通过@task()装饰的方法为一个事务。方法的参数用于指定该行为的执行权重。参数越大每次被虚拟用户执行的概率越高。如果不设置默认为1。
TaskSet子类中定义任务信息时,采取两种方式:@task装饰器和tasks属性。
采用@task装饰器定义任务信息时:
- from locust import TaskSet, task
- class UserBehavior(TaskSet):
- @task(1)
- def test_job1(self):
- self.client.get('/test1')
- @task(3)
- def test_job2(self):
- self.client.get('/test2')
采用tasks属性定义任务信息时
- from locust import TaskSet
- def test_job1(obj):
- obj.client.get('/test1')
- def test_job2(obj):
- obj.client.get('/test2')
- class UserBehavior(TaskSet):
- tasks = {test_job1:1, test_job2:3}
- # tasks = [(test_job1,1), (test_job1,3)] # 两种方式等价
上面两种定义任务信息方式中,均设置了权重属性,即执行test_job2的频率是test_job1的两倍。
若不指定,默认比例为1:1。
高级用法:
关联
在某些请求中,需要携带之前response中提取的参数,常见场景就是session_id。Python中可用通过re正则匹配,对于返回的html页面,可用采用lxml库来定位获取需要的参数;
- from locust import HttpLocust, TaskSet, task
- from lxml import etree
- class WebsiteTasks(TaskSet):
- def get_session(self,html): #关联例子
- tages = etree.HTML(html)
- return tages.xpath("//div[@class='btnbox']/input[@name='session']/@value")[0]
- def on_start(self):
- html = self.client.get('/index')
- session = self.get_session(html.text)
- payload = {
- "username": "test_user",
- "password": "123456",
- 'session' : session
- }
- header = {
- "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
- }
- self.client.post("/login",data=payload,headers=header)
- @task(5)
- def index(self):
- self.client.get("/")
- assert response['ErrorCode']==0 #断言
- @task(1)
- def about(self):
- self.client.get("/about/")
- class WebsiteUser(HttpLocust):
- host = "https://github.com/"
- task_set = WebsiteTasks
- min_wait = 5000
- max_wait = 15000
参数化
作用:循环取数据,数据可重复使用
例如:模拟3个用户并发请求网页,共有100个URL地址,每个虚拟用户都会依次循环加载100个URL地址

- from locust import TaskSet, task, HttpLocust
- class UserBehavior(TaskSet):
- def on_start(self):
- self.index = 0
- @task
- def test_visit(self):
- url = self.locust.share_data[self.index]
- print('visit url: %s' % url)
- self.index = (self.index + 1) % len(self.locust.share_data)
- self.client.get(url)
- class WebsiteUser(HttpLocust):
- host = 'http://debugtalk.com'
- task_set = UserBehavior
- share_data = ['url1', 'url2', 'url3', 'url4', 'url5']
- min_wait = 1000
- max_wait = 3000
保证并发测试数据唯一性,不循环取数据;
所有并发虚拟用户共享同一份测试数据,并且保证虚拟用户使用的数据不重复;
例如:模拟3用户并发注册账号,共有9个账号,要求注册账号不重复,注册完毕后结束测试:

采用队列
- from locust import TaskSet, task, HttpLocust
- import queue
- class UserBehavior(TaskSet):
- @task
- def test_register(self):
- try:
- data = self.locust.user_data_queue.get()
- except queue.Empty:
- print('account data run out, test ended.')
- exit(0)
- print('register with user: {}, pwd: {}' .format(data['username'], data['password']))
- payload = {
- 'username': data['username'],
- 'password': data['password']
- }
- self.client.post('/register', data=payload)
- class WebsiteUser(HttpLocust):
- host = 'http://debugtalk.com'
- task_set = UserBehavior
- user_data_queue = queue.Queue()
- for index in range(100):
- data = {
- "username": "test%04d" % index,
- "password": "pwd%04d" % index,
- "email": "test%04d@debugtalk.test" % index,
- "phone": "186%08d" % index,
- }
- user_data_queue.put_nowait(data)
- min_wait = 1000
- max_wait = 3000
保证并发测试数据唯一性,循环取数据;
所有并发虚拟用户共享同一份测试数据,保证并发虚拟用户使用的数据不重复,并且数据可循环重复使用;
例如:模拟3个用户并发登录账号,总共有9个账号,要求并发登录账号不相同,但数据可循环使用;

- from locust import TaskSet, task, HttpLocust
- import queue
- class UserBehavior(TaskSet):
- @task
- def test_register(self):
- try:
- data = self.locust.user_data_queue.get()
- except queue.Empty:
- print('account data run out, test ended')
- exit(0)
- print('register with user: {0}, pwd: {1}' .format(data['username'], data['password']))
- payload = {
- 'username': data['username'],
- 'password': data['password']
- }
- self.client.post('/register', data=payload)
- self.locust.user_data_queue.put_nowait(data)
- class WebsiteUser(HttpLocust):
- host = 'http://debugtalk.com'
- task_set = UserBehavior
- user_data_queue = queue.Queue()
- for index in range(100):
- data = {
- "username": "test%04d" % index,
- "password": "pwd%04d" % index,
- "email": "test%04d@debugtalk.test" % index,
- "phone": "186%08d" % index,
- }
- user_data_queue.put_nowait(data)
- min_wait = 1000
- 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进行判断;
- @task
- def all_interface(self):
- #豆瓣图书api为例子
- with self.client.get("https://api.douban.com/v2/book/1220562",name="/LhcActivity/GetActConfig",catch_response=True) as response:
- assert response.json()['rating']['max']==10 #python断言对接口返回值中的max字段进行断言
- if response.status_code ==200: #对http响应码是否200进行判断
- response.success()
- else:
- response.failure("GetActConfig[Failed!]")
五、Locust运行模式
运行Locust
时,通常会使用到两种运行模式:单进程运行和多进程分布式运行。
单进程运行模式
Locust
所有的虚拟并发用户均运行在单个Python
进程中,具体从使用形式上,又分为no_web
和web
两种形式。该种模式由于单进程的原因,并不能完全发挥压力机所有处理器的能力,因此主要用于调试脚本和小并发压测的情况。
当并发压力要求较高时,就需要用到Locust
的多进程分布式运行模式。从字面意思上看,大家可能第一反应就是多台压力机同时运行,每台压力机分担负载一部分的压力生成。的确,Locust
支持任意多台压力机(一主多从)的分布式运行模式,但这里说到的多进程分布式运行模式还有另外一种情况,就是在同一台压力机上开启多个slave
的情况。这是因为当前阶段大多数计算机的CPU都是多处理器(multiple processor cores
),单进程运行模式下只能用到一个处理器的能力,而通过在一台压力机上运行多个slave
,就能调用多个处理器的能力了。比较好的做法是,如果一台压力机有N
个处理器内核,那么就在这台压力机上启动一个master
,N
个slave
。当然,我们也可以启动N
的倍数个slave
,但是根据我的试验数据,效果跟N
个差不多,因此只需要启动N
个slave
即可。
no_web形式启动locust:
如果采用no_web
形式,则需使用--no-web
参数,并会用到如下几个参数。
-c, --clients
:指定并发用户数;
-n, --num-request
:指定总执行测试次数;
-r, --hatch-rate
:指定并发加压速率,默认值位1。
示例:
$ locust -f locustfile.py --host = xxxxx.com --no-web -c 1 -n 2
在此基础上,当我们想要调试Locust
脚本时,就可以在脚本中需要调试的地方通过print
打印日志,然后将并发数和总执行次数都指定为1
$ locust -f locustfile.py --host = xxxxx.com --no-web -c 1 -n 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
参数。
- D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --master --port=8089
- [2018-06-05 15:36:30,654] dengshihuang/INFO/locust.main: Starting web monitor at *:8089
- [2018-06-05 15:36:30,684] dengshihuang/INFO/locust.main: Starting Locust 0.8.1
启动slave
时需要使用--slave
参数;在slave
中,就不需要再指定端口了。master
启动后,还需要启动slave
才能执行测试任务。
- D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --slave
- [2018-06-05 15:36:30,654] dengshihuang/INFO/locust.main: Starting web monitor at *:8089
- [2018-06-05 15:36:30,684] dengshihuang/INFO/locust.main: Starting Locust 0.8.1
- D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --slave --master-host=<locust_machine_ip>
master
和slave
都启动完毕后,就可以在浏览器中通过http://locust_machine_ip:8089
进入Locust
的Web管理页面了。使用方式跟单进程web
形式完全相同,只是此时是通过多进程负载来生成并发压力,在web
管理界面中也能看到实际的slave
数量。如果slave
与master
不在同一台机器上,还需要通过--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:是每秒钟请求的个数。
相比于LoadRunner
,Locust
的结果展示十分简单,主要就四个指标:并发数
、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 QPS
golang net/http 约 45K QPS
ab 大约 15K QPS
locust 最差,而且response time明显比较长
-------------------------------------------------------------------
好文推荐:
1、https://debugtalk.com/post/locustplus-talk-about-performance-test/
这篇博客从性能测试方法、性能瓶颈定位、性能测试工具的基本组成、性能测试工具推荐(比较了loadrunner,jmeter,Locust优缺点)等方面做了深入的介绍,推荐!

蝗虫比Jmeter好的一点就是高并发,但是相对的不好的地方也有,就是需要另外的工具去监控服务器,而且需要去编写代码。
2、https://testerhome.com/topics/7607
3、https://testerhome.com/topics/7631
4、https://www.jianshu.com/p/0477c5007ce9
5、http://www.cnblogs.com/walker-dead-cave/p/5770200.html
6、https://www.cnblogs.com/fnng/p/6081798.html
7、https://www.jianshu.com/p/e4aaa23d3cc7
8、http://myzhan.github.io/
9、http://www.testclass.net/locust
10、https://www.cnblogs.com/qingchengzi/p/9505548.html