整理自:
进程/线程/协程:一文读懂什么是进程、线程、协程(建议收藏)
locust相关:
Installation — Locust 2.13.0 documentation(官方)
深入浅出开源性能测试工具 Locust(使用篇) - DebugTalk
Locust使用
需要了解的:
进程Process:应用程序运行时会在内存空间形成一块拥有独立地址的内存体,是操作系统分配系统资源的最小单位。一个应用程序可有多个进程,一个进程可有多个线程,但至少有一个线程,同一进程的所有线程共享该进程的所有资源。
线程Thread:应用程序执行中一个单一的顺序控制流程,是CPU调度执行的最小单位。一个线程就是执行一个子程序(函数)。一个线程只能属于一个进程,线程不能脱离于进程而存在。
协程Coroutines:完全由应用程序控制,而不是由操作系统内核来管理的更轻量级存在。一个线程可以有多个协程。协程在子程序内部是可中断的,转而执行别的子程序,然后到合适的时候再回来接着执行。
一、locust压测工具
1.1 介绍
开源的、使用python开发、基于事件、支持分布式、提供WEB UI、支持结果导出;
使用python第三方库gevent提供的非阻塞IO和协程Coroutine来实现网络层的并发请求;
采用python的requests库作为客户端;
说明:locust虽然是基于协程请求,但由于locust是运行在python解析器上,所以存在GIL锁机制;默认的网络请求库是requests同步库,所以单进程下的并发不会很高,如果想用locust进行并发请求,必定要使用分布式+异步请求库(同步请求和异步请求)。
1.2 如果非常感兴趣
locust源码:GitHub - locustio/locust: Scalable load testing tool written in Python(可以点击setup.py去查看依赖了哪些python库)
1.3 常用的压测工具对比图
二、locust安装
2.1 mac下的安装方式(使用pip工具安装)
第一种方式:
pip install locust(这里会默认安装最新版本,已经到1.6.0了,与0.*版本的差异挺大,以下都是讲的新版本locust使用)
pip install pyzmq(安装第三方库,让locust能多进程/多机器分布式进行压测)
locust --help(查看locust命令帮助并验证是否安装成功)
第二种方式:
通过GitHub上克隆项目安装(Python3推荐):GitHub - locustio/locust: Scalable load testing tool written in Python ,然后执行python setup.py install
2.2 执行命令及参数说明
执行命令如下:
master: locust -f ***x.py --master --master-bind-port 9800 --headless -u 500 -r 200 --expect-worker 10 -t 1m -s 5 --csv locustlog/
slave: locust -f ***x.py --master-host localhost --master-port 9800 --headless --worker
参数说明如下:
Command line |
Configfile |
Description |
中文对照 |
-f, --locustfile |
locustfile |
Python module file to import, e.g. ‘../other.py’. Default: locustfile |
需要执行脚本的文件及路径 |
-H, --host |
host |
Host to load test in the following format: http://10.21.32.33 |
压测主机的host |
-u, --users |
users |
Number of concurrent Locust users. Only used together with –headless |
压测并发用户数, 只能与-headless参数一起使用 |
-r, --hatch-rate |
hatch-rate |
The rate per second in which users are spawned. Only used together with –headless |
每秒启动用户数, 仅与–headless一起使用 |
-t, --run-time |
run-time |
Stop after the specified amount of time, e.g. (300s, 20m, 3h, 1h30m, etc.). Only used together with –headless |
压测执行时间, 仅与–headless一起使用 |
--web-host |
web-host |
Host to bind the web interface to. Defaults to ‘*’ (all interfaces) |
网页上执行压测的host, 默认为* |
--web-port, -P |
web-port |
Port on which to run web host |
网页上执行压测的端口 |
--headless |
headless |
Disable the web interface, and instead start the load test immediately. Requires -u and -t to be specified. |
不支持网页压测,需要指定 -u,-t参数一起使用 |
--web-auth |
web-auth |
Turn on Basic Auth for the web interface. Should be supplied in the following format: username:password |
网页执行压测认证, 格式为:username:password |
--tls-cert |
tls-cert |
Optional path to TLS certificate to use to serve over HTTPS |
通过HTTPS服务的TLS证书的路径 |
--tls-key |
tls-key |
Optional path to TLS private key to use to serve over HTTPS |
通过HTTPS服务的TLS私钥的路径 |
--master |
master |
Set locust to run in distributed mode with this process as master |
分布式运行的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进程运行的host, 默认为*,只能与-master一起使用 |
--master-bind-port |
master-bind-port |
Port that locust master should bind to. Only used when running with –master. Defaults to 5557 |
master进程运行的端口, 默认为5557,只能与-master一起使用 |
--expect-workers |
expect-workers |
How many workers master should expect to connect before starting the test (only when –headless used). |
分布式压测的slave个数, 只能与–headless参数一起使用 |
--worker |
worker |
Set locust to run in distributed mode with this process as worker |
slave压测进程 |
--master-host |
master-host |
Host or IP address of locust master for distributed load testing. Only used when running with –worker. Defaults to 127.0.0.1. |
salve压测进程配置的master进程的host, 只能与-worker一起使用 |
--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 –worker. Defaults to 5557. |
salve压测进程配置的master进程的端口, 默认为5557,只能与-worker一起使用 |
-T, --tags |
tags |
List of tags to include in the test, so only tasks with any matching tags will be executed |
需要被执行的标签 |
-E, --exclude-tags |
exclude-tags |
List of tags to exclude from the test, so only tasks with no matching tags will be executed |
需要被过滤的标签 |
--csv |
csv |
Store current request stats to files in CSV format. Setting this option will generate three files: [CSV_PREFIX]_stats.csv, [CSV_PREFIX]_stats_history.csv , [CSV_PREFIX]_failures.csv |
将请求的统计数据存到csv文件里, 该参数会保存三个csv文件 |
--csv-full-history |
csv-full-history |
Store each stats entry in CSV format to _stats_history.csv file |
将每个统计条目存 到 _stats_history.csv 文件 |
--print-stats |
print-stats |
Print stats in the console |
在控制台中打印统计信息 |
--only-summary |
only-summary |
Only print the summary stats |
只打印汇总统计 |
--reset-stats |
reset-stats |
Reset statistics once hatching has been completed. should be set on both master and workers when running in distributed mode |
重置统计信息,master和slave 同时设置才生效 |
--skip-log-setup |
skip-log-setup |
Disable Locust’s logging setup. Instead, the configuration is provided by the Locust test or Python defaults. |
禁用日志 |
--loglevel, -L |
loglevel |
Choose between DEBUG/INFO/WARNING/ERROR/CRITICAL. Default is INFO. |
记录日志级别,默认info |
--logfile |
logfile |
Path to log file. If not set, log will go to stdout/stderr |
日志记录目录,默认在/stdout/stderr |
--step-load |
step-load |
Enable Step Load mode to monitor how performance metrics varies when user load increases. Requires –step-users and –step-time to be specified. |
分布式压测逐步增加模式 |
--step-users |
step-users |
User count to increase by step in Step Load mode. Only used together with –step-load |
逐步增加的用户数, 只能和--step-load参数使用 |
--step-time |
step-time |
Step duration in Step Load mode, e.g. (300s, 20m, 3h, 1h30m, etc.). Only used together with –step-load |
逐步增加的压测时长, 只能和--step-load参数使用 |
--exit-code-on-error |
exit-code-on-error |
Sets the process exit code to use when a test result contain any failure or error |
配置退出执行标签, 当程序执行出错包含这个标签时 |
-s, --stop-timeout |
stop-timeout |
Number of seconds to wait for a simulated user to complete any executing task before exiting. Default is to terminate immediately. This parameter only needs to be specified for the master process when running Locust distributed. |
设置等待模拟用户完成任务时间, 默认立即退出 |
三、locust的基础使用
3.1 locust主要库组成
gevent:在Python中实现协程的第三方库。协程又叫微线程Corouine。使用gevent可以获取极高的并发能力;
flask:Python的一个web开发框架,和django相当;
requests:支持http/https访问的库;
msgpack-python:一种快速、紧凑的二进制序列化格式,使用与类似json的数据;
six:提供了一些简单的工具封装Python2和Python3 之间的差异;
pyzmq:安装这个第三方库,可以把Locust运行在多个进程或多个机器(分布式)
说明:安装locust时会自动检测我们当前python环境中是否已安装这些库;若无,则会安装符合版本要求的库。我们也可以自己先按版本要求把这些库装上,这样再装locust会很快。(库版本要求可在git上查看)
3.2 locust运行模式
单进程运行:Locust所有的虚拟并发用户均运行在单个Python进程中。具体从使用形式上,又分为no_web和web两种形式。该模式由于单进程的原因,并不能完全发挥压力机所有处理器的能力,因此主要用于调试脚本和小并发压测的情况。
分布式运行(通过多进程负载来生成并发压力):多机负载(多台压力机同时运行,每台压力机分担负载一部分的压力生成)、单机多进程(在同一台压力机上开启多个slave的情况,因为当前阶段大多数计算机的CPU都是多处理器)。
说明:
1)关于单机多进程:单进程运行模式下只能用到一个处理器的能力。而通过在一台压力机上运行多个slave,就能调用多个处理器的能力了。比较好的做法是,如果一台压力机有N个处理器内核,那就在这台压力机上先启动一个master,然后开多个窗口逐一启动N个slave(我们也可以启动N的倍数个slave,但是根据试验数据经验,效果和N个差不多,因此只需启动N个slave即可)。
2)关于多机负载:需要在主机中使用--master标记来启用一个locust实例,但是master节点的机器不会发起请求,只会收集数据展示;然后在从机使用--worker标记启动一条到多台locust salve的机器节点,与标记----master-host一起使用(指出master机器的ip/hostname)
3.3 locust使用中必须提到的两个类
如下图所示:TaskSet类、HttpUser类
1)HttpUser类:HttpUser类是 Locust 1.x 版本中代替以前老版本中的HttpLocust类,是 User 类的子类,用来定义虚拟用户,让虚拟用户作为客户端具备请求能力。新版locust可以直接在User类下声明@task
client属性: a)对于http(s)协议:如上图中,我们为要模拟的用户定义一个WebsiteUser类,继承自HttpUser类。HttpUser类为每个用户提供了一个 client 属性,该属性是HttpSession的一个实例,可用于向负载测试的目标系统发出 HTTP 请求。当测试开始时,Locust 将为它的每个虚拟用户创建一个此类的实例,并且每个虚拟用户会在自己的 gevent 线程中运行这些实例。 b)对于非http(s)协议:需要通过编写钩子触发器events.
request_success
和 events.request_failure
事件来自定义客户端。具体见locust压测rpc协议。tasks属性(必填):通过指向一个TastSet类来添加任务,声明要执行的任务集属于哪个类。
host属性:被测系统的host。在类中没有指定host,则终端启动locust时要通过–host参数来指定。
min_wait/max_wait:每个用户执行两个任务的时间间隔上下限(单位是毫秒),具体数值在上下限中随机取值。不指定时默认时间间隔为1秒。(或者使用wait_time=between(***, ***))
2)TaskSet类:用于定义用户的行为,主要实现了用户执行任务的调度算法(包括规划任务执行顺序schedule_task
、挑选下一个任务execute_next_task
、执行任务execute_task
、休眠等待wait
、中断控制interrupt
等)。需要在User相关类中定义后才能使用(如上图),因此locust 1.*已经不推荐使用这种,而是推荐都在HttpUser中定义(如下图)。
上图新版代码相关说明:
HttpUser类是User类的子类,能够直接在User类下声明@task。
wait_time 属性可设置用户在两个任务之间的思考时间,从这个区间中随机取值。更多wait_time 的参数可参考:https://docs.locust.io/en/latest/writing-a-locustfile.html#wait-time
self.client 和requests有一样的方法。
on_start方法是在一开始执行时,会执行的一个方法,并且只执行一次,和它对应的还有一个on_stop 是在执行结束后调用的。应用场景:login,logout 注:新版移除了 Locust.setup、 Locust.teardown、 TaskSet.setup、 TaskSet.teardown,改用 on_test_start、on_test_stop
@tag(*tags)
装饰器的作用就是在执行时使用-T 或者--tags 1
,此时,将限制测试只执行tag标记的任务。
3.4 开始执行压测
具体需要查看locsut --help中的参数说明。
简单压测脚本示例test.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from locust import task,TaskSet,HttpUser,events
class WebsiteTasks(TaskSet):
# 声明下面是一个任务
@task
def recommend(self):
# self.client是TaskSet的成员,相当于一个request对象
res = self.client.get("/game/rank/recommend?scene_id=4084&pagesize=10")
print(res)
class WebsiteUser(HttpUser):
# 声明执行的任务集是哪个类
tasks = [WebsiteTasks]
host = "http://yx-test-01.bcc-bdbl.baidu.com:8500"
# 最小等待时间和最大等待时间 请求间的间隔时间
min_wait = 1000
max_wait = 2000
以下是简单使用,注意写清py文件的路径:
本地执行: 第一步:如果py文件中已经写明被测系统的host,则直接在终端locust -f test.py 第二步:此时可以通过http://localhost:8089或者http://0.0.0.0:8089打开locust的web界面,输入界面中的必填项,点击start后才会开始启动压测。 (locust的web界面默认端口为8089,如果被占用也可通过-P参数来指定)
开发机执行:开发机上运行一定要在终端指定--web-host参数!!!(否则会报错!) 1)单进程运行:
2)单机多进程: 第一步:先在施压机器上启动一个master;
第二步:然后在施压机器上开多个窗口逐一启动N个worker(启动的worker数量最好不要超过机器的处理器数量);
第三步:在web界面输入必填项,开始压测。(界面中可以看到开启的worker数,RPS即QPS)
3.5 locust的Web UI
四、初次使用可能失足
问题一:
从本地对某个机器发送请求:
直接使用locust -f test.py完全没有问题;
不过由于这里8089端口已经被占用,所以换一个8500,locust -f test.py -P 8500(然后通过如下地址访问locust的WEB UI界面,这里指定了端口)
但是,如果是在某个开发机上(比如阡陌机)向另一台机器发送请求,只写locust -f test.py,运行后会报如下错误:OSError: getsockaddrarg: bad family,locust的web界面也无法通过访问该地址打开
网上说该错误出现是由于ipv4和ipv6的原因,并提供了解决方式(使用python-thrift问题汇总_weixin_33912246的博客-CSDN博客),我企图按照该方式去解决,但是苦于不知道借来的机器的root账户密码,无法修改/etc/hosts文件而作罢(不过我觉得不是这个原因)。
后来,我仔细查看了locust使用帮助(locust —help),发现其中有一个指定WEB UI的参数选项,怀疑是这个方面的原因,因此我尝试使用了locust -f test.py --web-host=10.12.75.161来启动locust压测,发现成功了(注:这里的10.12.75.161是施压机器的ip,没有特意指定端口,直接使用的locust的web界面默认端口8089)。开发机上使用locust,一定要指定--web-host参数!!
问题二:
如果出现如下端口占用问题,解决方式为:
先执行命令找占用的端口:netstat -tunlp
然后执行命令杀死进程:kill -9 21309
PS:攻略问答卡压测
攻略问答卡压测脚本
攻略问答卡压测脚本test.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from locust import task,TaskSet,HttpUser,events,between
import json
import requests
import time,os,queue
from locust.exception import LocustError
from geventhttpclient import HTTPClient
from geventhttpclient.url import URL
import linecache
import random
class WebsiteTasks(TaskSet):
# 声明下面是一个任务
# def on_start(self):
# '''初始化数据'''
# url = URL("http://10.103.64.34:8707")
# self.http = HTTPClient(url.host)
@task
def recommend(self):
randomLine = random.randrange(1, 1488)
query = linecache.getline(r'rand_query_1k_uniq.txt', randomLine)
inputParams = {
'query': '原神攻击力暴击属性规律探究',
'srcid': '5859',
'tn': 'baidu',
'dspName': 'iphone',
'pn': '0',
'rn': '10'
}
inputParams['query'] = query
res = self.client.get("/digital-reading/game/game", params = inputParams)
print(json.dumps(res.json()))
print(res)
# def on_stop(self):
# '''销毁数据'''
# self.http.close()
class WebsiteUser(HttpUser):
# 声明执行的任务集是哪个类
tasks = [WebsiteTasks]
host = "http://10.103.64.34:8707"
# 最小等待时间和最大等待时间 请求间的间隔时间
min_wait = 1000
max_wait = 2000