Skip to content

第 17 章 · 性能调优与请求率提升

本章目标:理解 Locust 单进程的并发瓶颈,掌握 ulimit、FastHttpUser、--processes 多进程等手段来最大化请求吞吐量,并学会判断"瓶颈在 Locust 还是被测系统"。

17.1 Locust 的并发模型与瓶颈

Locust 基于 gevent 协程库实现高并发。每个虚拟用户运行在一个 greenlet 中,通过猴子补丁(monkey patching)让标准 I/O 操作变为非阻塞。

单进程架构的瓶颈:

text
┌─────────────────────────────┐
│  单个 Python 进程            │
│  ┌─────────────────────┐    │
│  │ gevent event loop   │ ← 所有用户共享一个事件循环
│  │ user1 user2 ... userN│    │
│  └─────────────────────┘    │
│  CPU: 只用一个核             │
│  内存: 用户数 × 每用户内存     │
└─────────────────────────────┘

当用户数达到数千时,单个 Python 进程的 CPU 会成为瓶颈。官方文档明确指出:当看到 CPU usage above 90%! 警告时,说明 Locust 自身已经过载,此时测得的响应时间不可信。

关键认知

压测工具自身过载时,测出的数据是失真的——你测量的是"Locust 处理能力"而不是"被测系统性能"。务必先确保压测机有余量。

17.2 提升文件描述符限制

每个并发 HTTP 连接占用一个文件描述符(fd)。Linux/macOS 默认 fd 上限通常只有 256 或 1024,远不够高并发压测使用:

bash
# 查看当前限制
ulimit -n

# 临时提升到 100000(当前 shell 有效)
ulimit -n 100000

# 永久修改:编辑 /etc/security/limits.conf
# * soft nofile 100000
# * hard nofile 100000

# macOS 特殊处理(需要在启动前执行)
sudo launchctl limit maxfiles 100000 200000

如果 fd 不够,会看到 Too many open files 错误——这是压测中最常见的环境问题之一。

17.3 使用 FastHttpUser 降低 CPU 开销

默认的 HttpUser 底层使用 requests 库,功能全面但 CPU 开销较高。Locust 内置了 FastHttpUser,基于 fasthttp 实现更高效的 HTTP 客户端:

python
from locust import HttpUser, task, between
from locust.contrib.fasthttp import FastHttpUser


class StandardUser(HttpUser):
    """传统方式 — requests 底层"""
    wait_time = between(1, 2)

    @task
    def get_data(self):
        self.client.get("/api/data")


class FastUser(FastHttpUser):
    """高效方式 — fasthttp 底层,CPU 占用显著降低"""
    wait_time = between(1, 2)
    host = "https://staging.example.com"

    @task
    def get_data(self):
        # API 与 HttpUser 基本一致
        self.client.get("/api/data")

    @task
    def post_data(self):
        self.client.post("/api/data", json={"key": "value"})
对比HttpUser (requests)FastHttpUser (fasthttp)
CPU 效率一般更高
连接池urllib3内置 fasthttp pool
Cookie 自动管理⚠️ 需手动处理部分场景
流式响应
推荐场景功能测试/低并发高并发纯吞吐量压测

切换建议

如果你的脚本只做简单的 GET/POST 且不需要 requests 特有功能(如流式下载),直接把 HttpUser 改成 FastHttpUser 就能提升单 worker 承载量。

17.4 多进程模式 --processes

从 Locust 2.x 开始,可以通过 --processes 参数一键启动多进程模式:

bash
# 方式一:自动按 CPU 核数启动 worker 进程
locust -f locustfile.py --processes -u 5000 -r 100 --headless

# 方式二:指定进程数
locust -f locustfile.py --processes 4 -u 5000 -r 100 --headless

# Web UI 模式同样支持
locust -f locustfile.py --processes

其工作原理是在同一台机器上启动多个 Locust worker 进程 + 1 个 master 进程:

text
┌────────────────── 单台机器 ──────────────────┐
│  master (调度+统计汇总)                       │
│    ├── worker-1 (gevent loop, CPU core 1)   │
│    ├── worker-2 (gevent loop, CPU core 2)   │
│    ├── worker-3 (gevent loop, CPU core 3)   │
│    └── worker-4 (gevent loop, CPU core 4)   │
└──────────────────────────────────────────────┘

手动分布式(跨机器)

当单台机器也无法满足需求时,使用 master-worker 分布式模式:

bash
# 在主控机器上启动 master
locust -f locustfile.py --master --headless -u 10000 -r 200

# 在每台压力机上启动 worker(指向 master IP)
locust -f locustfile.py --worker --master-host=192.168.1.100

17.5 判断瓶颈在哪一侧

增加并发后如果吞吐量没有线性增长,需要判断瓶颈在 Locust 还是被测系统:

症状瓶颈位置解决方案
Locust CPU > 90%Locust 自身用 FastHttpUser / 加 worker / 加机器
被测系统 CPU > 80%被测服务这就是你要找的性能上限
网络带宽打满网络换更高带宽的压力机或减少 payload
响应时间持续上升被测系统饱和记录拐点数据即为容量结论

快速诊断命令

bash
# 监控 Locust 进程 CPU 使用率
top -pid $(pgrep -f locust | head -1)

# 检查网络连接数
netstat -an | grep ESTABLISHED | wc -l

# 查看被测系统的资源(如果在同一 VPC)
ssh staging-server "top -bn1 | head -5"

本章小结

  • Locust 单进程基于 gevent,只能利用一个 CPU 核;
  • ulimit -n 100000 解决文件描述符不足问题;
  • FastHttpUserHttpUser CPU 效率更高,适合高并发纯吞吐场景;
  • --processes 一键启动多进程,跨机器用 --master/--worker
  • Locust CPU > 90% 时测得数据不可信——先解决压测端瓶颈。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. Locust 默认的 HttpUser 底层使用哪个 HTTP 客户端库?

2. 看到 "CPU usage above 90%" 警告时,正确的做法是什么?

3. --processes 4 参数的作用是什么?

4. 以下哪种情况说明瓶颈在被测系统而不是 Locust?

🛠️ 动手实践

  1. 对比测试:分别用 HttpUserFastHttpUser 以 500 并发访问同一接口,记录两者的 CPU 使用率和 RPS 差异。
  2. 将你的 ulimit -n 从默认值提升到 100000,然后尝试以 2000 并发运行,观察是否还有 Too many open files 错误。
  3. --processes 启动 4 个 worker,以 3000 并发对 httpbin.org 发起压测,记录单进程和多进程模式的 RPS 对比。

完成练习后,进入下一章:CI/CD 集成