第 3 章 · HttpUser 与任务编写
本章目标:掌握 HttpUser 的 client 属性用法(GET/POST/参数传递),学会用 name 参数聚合统计,理解 on_start 生命周期钩子。
3.1 HttpUser 的核心属性
python
from locust import HttpUser, task
class ApiUser(HttpUser):
host = "https://httpbin.org" # 默认目标主机
@task
def make_request(self):
# self.client 是 HttpSession 实例
resp = self.client.get("/get")| 属性 | 类型 | 说明 |
|---|---|---|
client | HttpSession | 发 HTTP 请求的入口,API 兼容 requests |
host | str | 目标主机,可被命令行 -H 覆盖 |
environment | Environment | 当前测试环境对象(访问 runner、stats 等) |
3.2 发送各类请求
self.client 支持全部 HTTP 方法:
python
@task
def http_methods(self):
# GET 带查询参数
self.client.get("/get", params={"page": 1, "size": 20})
# POST 带 JSON 体
self.client.post("/post", json={"username": "locust", "active": True})
# PUT / DELETE / PATCH 同理
self.client.put("/put", json={"id": 1})
self.client.delete("/delete")
self.client.head("/get")带请求头:
python
@task
def with_headers(self):
self.client.get(
"/headers",
headers={"Authorization": "Bearer my-token", "X-Custom": "value"},
)3.3 用 name 聚合统计
这是初学者最容易忽略但最重要的技巧。看下面的例子:
python
# ❌ 不推荐:每个商品 ID 会生成一条独立统计
self.client.get(f"/product/{product_id}")
# ✅ 推荐:所有商品页归入同一条 "/product" 统计
self.client.get(f"/product/{product_id}", name="/product")如果不设 name,压测 100 个不同商品会产出 100 行统计记录——每行的样本量太小,百分位数据毫无参考价值。用 name 把同类请求合并后,统计才有意义。
python
@task
def browse_products(self):
for product_id in range(10):
self.client.get(
f"/api/products/{product_id}",
name="/api/products/[id]", # 归组后的统计名
)3.4 校验响应
Locust 默认只把 HTTP 状态码非 2xx 记为失败。业务级校验需要手动处理:
python
from locust import HttpUser, task
class UserApi(HttpUser):
host = "https://httpbin.org"
@task
def get_user(self):
with self.client.get("/json", catch_response=True) as resp:
if resp.status_code != 200:
resp.failure(f"状态码异常: {resp.status_code}")
elif "slideshow" not in resp.text:
resp.failure("响应体缺少预期字段")
else:
resp.success()关键点:
catch_response=True让你接管成功/失败的判定权;resp.failure("消息")将该次请求标记为逻辑失败(即使 HTTP 200);- 这在验证 API 返回的业务码时必不可少。
REST API 校验模式
官方文档推荐对 JSON API 使用如下模板:先检查状态码,再解析 JSON 验证业务字段,最后在 else 分支标记成功。
3.5 on_start 与登录态管理
on_start 在每个虚拟用户启动时执行一次——是放置登录逻辑的标准位置:
python
class AuthenticatedUser(HttpUser):
host = "https://httpbin.org"
def on_start(self):
"""每个虚拟用户启动时调用一次"""
resp = self.client.post(
"/login",
json={"username": "load_test", "password": "secret"},
)
token = resp.json().get("access_token")
self.client.headers.update({"Authorization": f"Bearer {token}"})
@task
def access_protected(self):
self.client.get("/bearer") # 自动携带上面设置的 header
def on_stop(self):
"""每个虚拟用户停止时调用一次"""
pass由于每个用户有独立的 client 实例,登录后设置的 headers 只影响当前用户——天然实现了会话隔离。
本章小结
client支持 GET/POST/PUT/DELETE/PATCH/HEAD,API 兼容 requests;- 用
name="/pattern"把动态 URL 归入同一统计行,保证百分位数据有效; catch_response=True+resp.failure()实现业务级断言;on_start放登录逻辑,每虚拟用户执行一次;on_stop做清理。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 为什么要在 client.get() 中使用 name 参数?
2. API 返回 HTTP 200 但业务码 code=5000,如何让 Locust 把它记为失败?
3. on_start 方法什么时候被调用?
4. 登录后要让该用户的所有后续请求自动携带 Authorization 头,最佳做法是?
🛠️ 动手实践
- 编写一个压测脚本:
on_start登录获取 token,后续请求携带 Authorization 头访问/bearer。 - 对
https://httpbin.org/json做 catch_response 校验:检查响应包含"slideshow"字段,否则标记 failure。 - 用
name="/delay/[n]"把/delay/1、/delay/2、/delay/3三个请求归为一组,观察统计表的变化。
完成后进入第 4 章。