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 解决文件描述符不足问题;
  • FastHttpUser 比 HttpUser CPU 效率更高,适合高并发纯吞吐场景;
  • --processes 一键启动多进程,跨机器用 --master/--worker;
  • Locust CPU > 90% 时测得数据不可信——先解决压测端瓶颈。

🧪 随堂测验

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

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

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

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

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

🛠️ 动手实践 ​

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

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