Locust压测需显式启用--master/--worker分布式模式,单机默认单节点;wait_time、users、spawn_rate三者不匹配会导致RPS远低于预期;gevent monkey patch必须置于所有import之前。

Locust 压测不是“多开几个进程就并发上去了”,它依赖协程调度和主从角色分工,单机默认就是单节点;想突破几千并发,必须用 --master/--worker 显式分布式,且 wait_time、users、spawn_rate 三者不匹配时,RPS 会远低于预期。
Locust 分布式压测必须显式启用 --master/--worker 模式
很多人在本地开多个终端运行 locust -f locustfile.py,以为这就是分布式——其实不是。Locust 的分布式是明确的 C/S 架构:--master 节点只做调度和聚合,不发请求;--worker 节点才真正发压。不加参数永远只是单机模式。
常见失败现象:
-
Waiting for master to start:worker 启动后卡住,说明网络不通或--master-host写错(不能带http://,只能是 IP 或主机名) - WebSocket 握手失败但日志无报错:master 和 worker 的 Locust 版本不一致(比如 master 是 2.31.0,worker 是 2.15.1)
- Web UI 显示 users=0 且 RPS=0:master 启动时
locustfile.py有语法错误,但错误提示可能被吞掉,只报ImportError
正确启动方式:
立即学习“Python免费学习笔记(深入)”;
# 在 master 主机上运行(监听 8089 和 5557 端口) locust -f locustfile.py --master --host=http://api.example.com <h1>在 worker 主机上运行(指定 master 地址,不带 http://)</h1><p>locust -f locustfile.py --worker --master-host=192.168.1.100
wait_time、users、spawn_rate 共同决定真实 RPS
很多人设了 --users=1000 就以为能打出 1000 QPS,结果实际只有 200。根本原因是忽略了 wait_time 对单个用户请求频率的限制。
举例说明:
-
wait_time = between(1, 3)→ 单个用户平均 2 秒发一次请求 → 1000 用户理论 RPS ≈ 500 -
--spawn-rate=10表示每秒新增 10 个用户,拉满 1000 用户需 100 秒;若压测只跑 30 秒,实际并发远不到 1000 -
@task(10)不是“执行 10 次”,而是相对权重:两个任务权重为 10 和 1,前者被选中的概率约是后者的 10 倍
调优建议:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 先固定
wait_time = constant(0.1)(即 100ms 思考时间),观察单用户极限 RPS,再反推目标并发数 - 压不出预期 RPS 时,优先检查
wait_time是否过大,其次看spawn_rate是否太小导致拉不满users - 避免把
wait_time设为constant(0),gevent 在极端高频下容易触发事件循环饥饿,反而降低吞吐
HttpUser.client 默认复用 session,但 token 等状态需手动维护
HttpUser.client 底层是 requests.Session,自动处理 Cookie,但像 JWT token、临时签名这类动态认证信息,必须由你显式存取和更新。
典型错误写法:
def on_start(self):
resp = self.client.post("/login", json={"u": "a", "p": "b"})
self.token = resp.json()["token"] # ✅ 正确:存在实例变量里
<p>@task
def api_call(self):</p><h1>❌ 错误:self.token 可能为 None(on_start 失败或未执行)</h1><pre class="brush:php;toolbar:false;"><code>self.client.get("/data", headers={"Authorization": f"Bearer {self.token}"})</code>安全做法:
- 在
@task内部加if not hasattr(self, "token"):容错判断 - 用
catch_response=True包裹关键请求,失败时主动response.failure("no token") - 避免在
on_start中做重试逻辑,它只执行一次;高频 token 刷新应放在 task 内或用events.request_failure钩子捕获
gevent monkey patch 必须放在所有 import 之前
Locust 依赖 gevent 实现协程,而 gevent 要求对标准库进行 monkey patch 才能接管 IO。如果 patch 放错位置,会出现:
- HTTP 请求阻塞主线程,CPU 占用低但并发上不去
- 部分系统调用(如
time.sleep、socket)仍走原生线程,导致 greenlet 调度失效
正确顺序(必须是文件最顶部):
from gevent import monkey monkey.patch_all() # ⚠️ 这行必须在 import requests / locust 之前 <p>import requests from locust import HttpUser, task, between
如果你用了第三方库(如 redis-py、pymysql),也要确认它们是否支持 gevent;不支持的库会退化为同步阻塞,成为压测瓶颈点。
真正卡住 Locust 并发上限的,往往不是代码逻辑,而是没意识到 wait_time 是每个用户的节奏控制器,或是 monkey.patch_all() 被埋在一堆 import 下面——这些细节不处理,再多的 worker 也跑不满 CPU。

















