Locust入门
Keep Team Lv1

一、什么是Locust

1.1 基础概念

Locust 是一个开源的、基于 Python 的分布式负载测试工具,用于测试网站、Web 应用程序和API的性能和可扩展性。它通过模拟大量并发用户访问目标系统,帮助开发者和测试人员识别系统在高负载下的表现和潜在瓶颈。

Locust中一个节点就可以在一个进程中支持数千并发用户,基于事件,通过gevent使用轻量级执行单元。

1.2 优势

  1. 纯 Python 编写:Locust 使用 Python 编写测试用例,灵活且易于维护。
  2. HTTP 请求:它是基于 requests 库发送 HTTP 请求。
  3. 协程运行:locust是使用协成运行的,用更低的成本实现更多的并发。并且是基于gevent实现的。
  4. 分布式支持:支持分布式,支持更多的压力测试。
  5. Web UI:内置了 Web UI,可通过浏览器进行控制和监控。
  6. 插件扩展:我们可以用第三方的插件,进行扩展。

如果你使用 Python 进行接口测试(尤其是使用 <font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">requests</font> 进行接口测试的),那么就优先考虑使用 Locust 进行接口性能测试,因为更方便。

1.3 简单使用

第一步,要准备一个可用的接口,后面需要把这个接口测试用例转换为性能测试用例。

  1. 安装Flask依赖
1
pip install flask
  1. 示例代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import random

from flask import Flask, make_response

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def run():
# 1/20 的概率返回失败,模拟接口高并发下出现问题
if random.randint(1, 20) == 1:
res = {
'code': 1,
'msg': "Error",
'data': None
}
else:
res = {
'code': 0,
'msg': "OK",
'data': {
'test': '测试页面'
}
}
return make_response(res)

if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=False, threaded=True)

运行后生成一个可访问的接口,我们后面就使用这个接口模拟性能测试。

第二步,准备一个接口测试用例 。

  1. 使用requests调用刚才创建的接口,并编写一个简单的测试用例。
1
2
3
4
5
6
7
8
9
10
11
12
import requests

def test_request():
resp = requests.get(url="http://127.0.0.1:8080/")
print(resp.json())
assert resp.status_code == 200

if __name__ == '__main__':
test_request()

# 执行结果
# {'code': 0, 'data': {'test': '测试页面'}, 'msg': 'OK'}

第三步,转化为性能测试用例。

将第二步的自动化测试用例转换为locust的测试用例。

  1. 创建一个类,继承 locust.HttpUser
  2. 使用 @locust.task 装饰器标识性能测试用例。
  3. 使用 self.client.get或者post 发送请求。
  4. 设置 wait_time = locust.between(1, 2) 添加locust给我们提供的函数between,里面1和2的值代表着每个task间隔1到2秒,相当于每个测试用例的间隔
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import locust

# 继承 HttpUser 这个类
class MyUser(locust.HttpUser):
# 添加用例等待时间,表示每个task间隔1-2s
wait_time = locust.between(1, 2)

@locust.task
def test_request(self):
# 使用 self.client 发送请求
with self.client.get(url='http://127.0.0.1:8080/', catch_response=True) as resp:
if resp.status_code == 200:
try:
msg = resp.json().get('msg')
if msg != 'OK':
# 模拟请求接口报错的情况
resp.failure(f"Unexpected msg: {msg}")
except Exception as e:
resp.failure(f"JSON parse error or missing 'msg': {e}")
# 其他状态码(如 500)已自动失败,无需处理

第四步,执行性能测试用例

  1. 通过Web UI执行(GUI模式)

通过Web UI来执行主要就是简洁方便,可以出具绘制图表来展示。但WebUI也有一个小弊端,那就是制作的图表会浪费一些性能,因为制作图表的时候会实时获取数据,这也是浪费性能的原因。

在终端运行下面命令:

1
2
3
4
5
6
7
locust -f test_xxx.py

# 如下输出
(.venv) E:\project\py\Test>locust -f xxx.py
[2025-12-24 21:17:52,271] ZAL/INFO/locust.main: Starting Locust 2.34.0
[2025-12-24 21:17:52,272] ZAL/WARNING/locust.main: Python 3.9 support is deprecated and will be removed soon
[2025-12-24 21:17:52,272] ZAL/INFO/locust.main: Starting web interface at http://localhost:8089, press enter to open your default browser.

打开浏览器访问日志给出的地址:

设置配置参数,参数说明如下:

  • **Number of users (peak concurrency)**:模拟的并发用户数,例如 1000。
  • **Ramp up (users spawned/second)**:用户启动速率。比如:我们选择了1000个用户,如果我们这里填写10,也就代表着每秒有10个用户在调用 。
  • **Host**:填写接口的url。但是我们在性能测试用例中已经写了URL,那么这个就无所谓了。写不写都可以,所以我们就随便写个1(写1的原因是因为这个为空的话会报错,也算是一个bug)
  • **Run time**:测试运行时间,例如 120s 表示运行2分钟。如果要一直运行那就不写。

开始执行后,可以在 <font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">STATISTICS</font>里面看到测试结果。

性能测试指标分析:

(1)请求总数与失败数

总请求数为 4835,失败请求数为 235,失败率较低,为4.86%。要根据具体要求选择是否排查。

(2)响应时间

中位数响应时间为 3 ms,平均响应时间为 5.89 ms。中位数响应时间相对较低,说明大部分请求的响应时间较快,但平均响应时间较高,可能是因为有少数请求的响应时间过长导致的。

95% 和 99% 的响应时间分别为 17 ms 和 19 ms,表明有 5% 和 1% 的请求响应时间超过了这些阈值,可能影响用户体验。

(3)最小值与最大值

最小响应时间为 1 ms,最大响应时间为 34 ms,最大响应时间的差异较大,说明在某些情况下,接口的响应时间会非常慢。

(4)数据大小与吞吐量

平均数据大小为 63.64 字节,当前每秒请求数 (RPS) 为 74.9,当前故障数为 4.2。吞吐量表现良好,但由于高失败率,实际可用的吞吐量是受到影响的。

具体的参数如下表:

性能参数 描述
Type 请求类型,如GET、POST
Name 请求路径
Requests 当前请求数量
Failes 请求失败数量
Median 中间值毫秒,一半的服务响应低于该值,另一半高于该值
95% 95%的请求响应时间
Average 平均值,单位毫秒,所有请求平均响应时间
Min 请求的服务器最小响应时间
Max 请求的服务器最大响应时间
Average Size 单个请求大小,字节
RPS 每秒能处理的请求数目

另外,执行完成后,还可以通过CHARS页面查看实时数据。

这里会显示的指标有每秒请求数每毫秒响应时间用户数量

还可以通过FAILURES查看接口请求的异常信息。

还有EXCEPTIONS代码异常。

  1. 通过命令行来执行(非GUI模式)

使用命令行来执行,就不会有页面和图表出现。所以会充分利用性能来进行测试接口。

常用的启动命令+参数

1
locust -f test_req.py --headless -u 1000 -r 10 --host=1 --run-time 120s 

启动之后会持续输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(.venv) E:\project\py\Test>locust -f xxx.py --headless -u 1000 -r 10 --host=1 --run-time 120s 
[2025-12-24 21:44:11,438] ZAL/INFO/locust.main: Starting Locust 2.34.0
[2025-12-24 21:44:11,439] ZAL/WARNING/locust.main: Python 3.9 support is deprecated and will be removed soon
[2025-12-24 21:44:11,439] ZAL/INFO/locust.main: Run time limit set to 120 seconds
Type Name # reqs # fails | Avg Min Max Med | req/s failures/s
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
Aggregated 0 0(0.00%) | 0 0 0 0 | 0.00 0.00

[2025-12-24 21:44:11,441] ZAL/INFO/locust.runners: Ramping to 1000 users at a rate of 10.00 per second
Type Name # reqs # fails | Avg Min Max Med | req/s failures/s
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
GET / 30 2(6.67%) | 9 2 17 8 | 0.00 0.00
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
Aggregated 30 2(6.67%) | 9 2 17 8 | 0.00 0.00

Type Name # reqs # fails | Avg Min Max Med | req/s failures/s
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
GET / 86 4(4.65%) | 7 1 17 7 | 12.50 1.00
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
Aggregated 86 4(4.65%) | 7 1 17 7 | 12.50 1.00

Type Name # reqs # fails | Avg Min Max Med | req/s failures/s
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
GET / 171 9(5.26%) | 8 1 31 6 | 18.50 1.00
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
Aggregated 171 9(5.26%) | 8 1 31 6 | 18.50 1.00

1.4 多接口测试

第一步,实现一个模拟登录的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from flask import Flask, make_response, request

app = Flask(__name__)

@app.route('/login', methods=['GET', 'POST'])
def login():
# 尝试从GET请求中获取参数
username = request.args.get('username', type=str)
password = request.args.get('password', type=int)

# 如果GET请求中没有参数,则尝试从POST请求中获取参数
if username is None or password is None:
username = request.form.get('username', type=str)
password = request.form.get('password', type=int)

# 验证用户名和密码
if username == 'admin' and password == 123:
response_data = {
'code': 0,
'msg': "OK",
'data': {
'test': f"{request.method.lower()}请求测试页面"
}
}
else:
response_data = {
'code': 999,
'data': f"无效的用户名或密码: {username} {password}"
}

return make_response(response_data)

if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=False, threaded=True)

第二步,编写多接口测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from locust import HttpUser, task, between

# 1、在类中继承locust.HttpUser的子类,也就是创建这个子类
class MyUser(HttpUser):

# 添加locust给我们提供的函数between,里面1和2的值代表着每个task间隔1到2秒
wait_time = between(1, 2)

@task(1) # 权重为1
def test_login_get(self):
resp = self.client.get('http://127.0.0.1:8080/login?username=admin&password=123')
print(resp.json())
assert resp.status_code == 200

@task(2) # 权重为2,执行频率更高
def test_login_post(self):
resp = self.client.post("http://127.0.0.1:8080/login", data={"username": "admin", "password": "123"})
print(resp.json())
assert resp.status_code == 200

参数解释:

  • HttpUser:Locust的核心类,用于模拟用户行为。
  • task:装饰器,用于标记测试任务。
  • between:用于定义任务之间的等待事件。
  • @task(1):定义了一个权重为1的任务,权重决定了任务的执行频率。
  • test_login_get:模拟GET请求。
  • test_login_post:模拟POST请求。

同样,<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">test_login_post</font>任务的权重为2,意味着它的执行频率是<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">test_login_get</font>的两倍。这种权重设置可以准确模拟不同用户行为的真实分布。

第三步,启动测试用例。

使用命令:

1
2
3
4
(.venv) E:\project\py\Test>locust -f xxx.py --host=http:127.0.0.1:8080/
[2025-12-24 21:57:14,255] ZAL/INFO/locust.main: Starting Locust 2.34.0
[2025-12-24 21:57:14,255] ZAL/WARNING/locust.main: Python 3.9 support is deprecated and will be removed soon
[2025-12-24 21:57:14,255] ZAL/INFO/locust.main: Starting web interface at http://localhost:8089, press enter to open your default browser.

然后跳转到Web页面,填写配置。

这里的选择是 模拟100个用户并发,每秒递增10个发起请求。

可以看到图片中我们的设置的@task(2)的接口数量是远超@task(1)的。

二、主流性能测试工具对比

工具 是否收费 语言 并发机制 单机并发能力 场景压测 分布式
Locust 免费 Python 协程 支持 支持
Jmeter 免费 Java 线程 支持 支持
Loadrunner 收费 C 进程/线程 支持 支持

三、解析Locust核心参数

为了充分发挥Locust的性能,我们需要深入理解其核心参数及其作用。以下是Locust中常用的一些关键参数及其详细解释。

3.1 用户类参数

3.1.1 HttpUser与User

  • HttpUser适用于需要发送HTTP请求的性能测试场景,内置了<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">self.client</font>用于发送请求。
  • User基本用户类,不自带<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">self.client</font>,适用于非HTTP协议的测试。

根据具体需求选择合适的用户类,可以提高测试的灵活性和效率。

3.1.2 tasks

1
tasks = {task1: weight1, task2: weight2, ...}

<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">tasks</font>属性定义了用户在测试过程中可以执行的多个任务及其权重。权重决定了任务被执行的概率和频率。例如:

1
tasks = {test_login_get: 1, test_login_post: 2}

表示<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">test_login_post</font>的执行频率是<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">test_login_get</font>的两倍。

3.1.3 wait_time

1
wait_time = between(min_wait, max_wait)

wait_time设置了用户在执行两个任务之间的等待时间。

Locust提供了多种等待时间函数:

  • between(min, max):在minmax秒之间随机等待。
  • constant(seconds):固定等待指定秒数。
  • constant_pacing(seconds):确保每个任务之间的间隔至少为指定秒数。

我们合理设置等待时间,可以更真实地模拟用户行为,避免过度负载。

3.2 命令行参数

Locust提供了丰富的命令行参数,用于灵活配置测试。

3.2.1 -f/–locustfile

指定locust的测试脚本文件。默认情况下,Locust会寻找locustfile.py

1
locust -f 测试代码.py

3.2.2 -u/–user

定义模拟的总用户数。例如,-u 100表示有100个并发用户。

1
locust -f 测试代码.py -u 100

3.2.3 -r/–spawn-rate

定义用户生成的速率,即每秒生成多少用户。例如,-r 10表示每秒生成10个用户。

1
locust -f 测试代码.py -r 10

3.2.4 –headless

以无头模式运行,不启动Web页面,适合自动化脚本和持续集成。

1
locust -f 测试代码.py --headless -u 100 -r 10

3.2.5 –run-time

定义测试的持续时间。例如,--run-time 1h表示测试持续1小时。

1
locust -f 测试代码.py --headless -u 100 -r 10 --run-time 1h

3.2.6 –csv

将测试结果保存为CSV文件,便于后续分析。

1
locust -f 测试代码.py -u 100 -r 10 --csv=performance_test

生成的文件将包括<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">performance_test_stats.csv</font><font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">performance_test_failures.csv</font>等,详细记录了请求的响应时间、失败率等关键指标。

3.3 SLAs(服务水平协议)

服务水平协议(SLAs)是定义预期性能标准的重要工具。Locust支持特定指标设置SLAs和自动监控和警报。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from locust import HttpUser, task, between, events

class MyUser(HttpUser):
wait_time = between(1, 2)

@task
def test_login_post(self):
resp = self.client.post("http://127.0.0.1:8080/login", data={"username": "admin", "password": "123"})
assert resp.status_code == 200
# 设定响应时间不超过500ms
if resp.elapsed.total_seconds() > 0.5:
events.request_failure.fire(
request_type="POST",
name="test_login_post",
response_time=resp.elapsed.total_seconds(),
exception="Response time exceeded SLA"
)

通过设定SLAs,就可以确保代码在规定的性能范围内运行,一旦超出,系统会自动记录失败事件,帮助我们及时发现和解决问题。