转载: https://www.cnblogs.com/ailiailan/p/9474973.html
一、写在前面
官方使用文档: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:workSpacesApiAutoTestTestCasesOpsUltraAPITestMonitorAPITest>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
中,就不需要再指定端口了。masterslave
才能执行测试任务。
D:workSpacesApiAutoTestTestCasesOpsUltraAPITestMonitorAPITest>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:workSpacesApiAutoTestTestCasesOpsUltraAPITestMonitorAPITest>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好的一点就是高并发,但是相对的不好的地方也有,就是需要另外的工具去监控服务器,而且需要去编写代码。