4. Locust 实战:从安装到分布式压测

Locust 这个工具,我用了好几年了。说实话,刚开始接触压测工具时,JMeter 用得比较多。但后来发现,用 Python 写压测脚本实在太灵活了。Locust 的核心思想很简单——每个用户就是一只蝗虫,你想想看,成千上万只蝗虫一起啃食你的系统,吞吐量自然就上去了。

这一章,我们直接上手。从安装开始,到写一个完整的 User 类,再到分布式压测和 Web UI 监控。我会把项目中踩过的坑都告诉你。

4.1 安装 Locust

安装没什么难度。用 pip 一行搞定:

pip install locust

我个人习惯用虚拟环境,避免污染全局 Python。如果你也这么干:

python -m venv locust_env
source locust_env/bin/activate  # Windows 用 locust_env\Scripts\activate
pip install locust

验证安装:

locust --version

看到版本号就说明成了。嗯,这里要注意——如果你在公司内网,记得配好代理或使用离线包。我曾经在客户现场折腾了半小时,才发现是网络问题。

4.2 编写第一个 User 类

Locust 的核心是 User 类。每个虚拟用户就是一个 User 实例。我们来看一个最简单的例子:

from locust import User, task, between

class MyUser(User):
    wait_time = between(1, 3)

    @task
    def hello(self):
        print("执行用户任务")

这个类干了三件事:

  • 继承 User 基类
  • 设置等待时间 wait_time,模拟用户思考间隔
  • 用 @task 装饰器标记任务方法

运行它:

locust -f my_user.py

然后打开浏览器访问 http://localhost:8089,就能看到 Web UI 了。

小技巧: wait_time 除了 between,还可以用 constant(固定间隔)或 constant_pacing(固定吞吐间隔)。我常用 between,更贴近真实用户行为。

4.3 HttpUser 与 TaskSet

实际压测中,我们压的是 HTTP 接口。Locust 提供了 HttpUser,它自带一个 client 属性,本质上是 requests.Session 的封装。

from locust import HttpUser, task, between

class WebsiteUser(HttpUser):
    wait_time = between(0.5, 2.5)

    @task
    def index(self):
        self.client.get("/")

    @task(3)
    def about(self):
        self.client.get("/about")

看到 @task(3) 了吗?这是权重。about 页面被访问的概率是 index 的 3 倍。我一般用权重来模拟不同接口的调用比例。

当任务变多时,TaskSet 就派上用场了。它可以把一组任务组织起来:

from locust import HttpUser, task, between, TaskSet

class UserBehavior(TaskSet):
    def on_start(self):
        """用户启动时执行一次,比如登录"""
        self.client.post("/login", {"username": "test", "password": "123"})

    @task(2)
    def view_items(self):
        self.client.get("/items")

    @task(1)
    def buy_item(self):
        self.client.post("/buy", {"item_id": 1})

class WebsiteUser(HttpUser):
    tasks = [UserBehavior]
    wait_time = between(1, 2)

on_start 方法很实用。我在项目中用它做登录、获取 token、初始化数据。这样每个虚拟用户启动时都会先登录,后续任务带着 token 跑。

注意: on_start 里的操作会计入压测数据。如果你不想让登录请求污染结果,可以在统计时过滤掉。我一般单独记录登录的成功率。

4.4 分布式压测

单机压测,说白了就是一台机器模拟用户。但当你需要上万并发时,单机的 CPU、内存、网络连接数都会成为瓶颈。这时候就需要分布式压测。

Locust 的分布式架构很简单:

  • Master 节点:负责协调、收集数据、提供 Web UI
  • Worker 节点:真正产生压力的机器

启动方式:

# 先启动 master
locust -f my_user.py --master

# 再启动 worker(可以多台机器)
locust -f my_user.py --worker --master-host=192.168.1.100

这里有几个关键点:

参数 说明 我的建议
--master 以主节点模式运行 master 本身不产生压力,资源消耗很小
--worker 以工作节点模式运行 worker 机器配置越高越好,尤其是 CPU
--master-host 指定 master 的 IP 确保 worker 能访问 master 的 5557 端口
--expect-workers 期望连接的 worker 数量 等所有 worker 就绪后再开始压测
避坑指南: 我曾经在分布式压测时,worker 和 master 的时间不同步,导致数据对不上。后来养成了压测前先 ntpdate 同步时间的习惯。另外,worker 的脚本必须和 master 完全一致,否则会报错。

下面这张图展示了 Locust 的分布式架构:

Locust 分布式压测架构 Master 节点 Web UI + 数据汇总 Worker 1 192.168.1.101 Worker 2 192.168.1.102 Worker N 192.168.1.10x 说明: Master 只负责调度和展示,不产生压力。Worker 才是真正的压测引擎。 所有 Worker 共享同一个压测脚本,并发数由 Master 统一分配。

4.5 Web UI 监控

启动 Locust 后,默认会在 8089 端口开启 Web UI。这个界面很直观:

  • Start 页面:设置总用户数、每秒孵化数、目标 Host
  • Statistics 页面:实时显示每个接口的请求数、失败数、响应时间、RPS
  • Charts 页面:折线图展示吞吐量、响应时间、用户数变化
  • Failures 页面:列出所有失败的请求及错误原因
  • Exceptions 页面:显示脚本中抛出的异常

我一般重点关注三个指标:

指标 含义 健康值参考
RPS 每秒请求数,即吞吐量 越高越好,但要看系统承受能力
95% 响应时间 95% 的请求在多少毫秒内完成 一般要求 < 500ms
失败率 失败请求占比 应 < 1%,最好为 0
实战经验: 压测时别只盯着平均值。平均值会掩盖问题。我习惯看 95% 和 99% 分位值。有一次系统平均响应时间只有 200ms,但 99% 分位值到了 3 秒——说明有少量请求被严重阻塞了。

Web UI 还支持 CSV 导出。压测结束后,点一下「Download Data」就能拿到原始数据。我通常用这些数据做后续分析,或者生成报告。

4.6 命令行模式

有时候你不想开浏览器,或者要在 CI/CD 里跑压测。Locust 支持无头模式:

locust -f my_user.py --headless -u 1000 -r 100 --run-time 5m --host=https://example.com

参数说明:

  • -u 1000:模拟 1000 个用户
  • -r 100:每秒孵化 100 个用户
  • --run-time 5m:运行 5 分钟
  • --host:目标地址

运行结束后,终端会直接打印统计结果。我经常把它写进 Jenkins 任务里,每天凌晨跑一次,监控系统性能变化。

注意: 无头模式下,如果压测时间太长,终端输出会刷屏。建议加上 --only-summary,只在结束时打印汇总数据。或者用 --csv 导出到文件。