Skip to content

第 4 章 · 任务权重与等待时间

本章目标:精确控制虚拟用户的行为分布——用 @task 权重和 tasks 列表控制任务选择概率,用 between/constant/constant_pacing 三种策略控制执行节奏。

4.1 任务权重的两种写法

装饰器方式

python
from locust import HttpUser, task


class MallUser(HttpUser):
    host = "https://httpbin.org"

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

    @task(3)  # 选中概率是上面的 3 倍
    def search_product(self):
        self.client.get("/get?q=phone")

    @task(2)
    def add_to_cart(self):
        self.client.post("/post", json={"item": "phone", "qty": 1})

总权重 = 1+3+2 = 6,各任务被选中概率:首页 16.7%、搜索 50%、购物车 33.3%。

tasks 列表方式

python
class WeightedUser(HttpUser):
    host = "https://httpbin.org"
    tasks = {view_homepage: 1, search_product: 3}  # 字典 {方法: 权重}

也可以用列表表示重复次数(等价于权重):

python
tasks = [search_product] * 3 + [view_homepage]

两种方式效果相同,装饰器更直观、字典更适合动态场景。

4.2 三种等待时间策略

wait_time 决定了任务之间的停顿,直接决定 RPS 的计算模型:

python
from locust import HttpUser, task, between, constant, constant_pacing


class WaitStrategies(HttpUser):
    host = "https://httpbin.org"

    # 策略一:随机区间(最常用)
    wait_time = between(1, 5)

    @task
    def demo(self):
        self.client.get("/get")
策略写法行为
between(a, b)每次随机等 a~b 秒最接近真实人类行为
constant(t)固定等 t 秒简单可控
constant_pacing(t)任务每 t 秒运行一次(含执行时间)精确控制迭代速率

constant vs constant_pacing 的区别

这是最容易混淆的一对:

python
# constant(3):任务执行完后固定再等 3 秒
# 如果任务耗时 2s,实际周期 = 2 + 3 = 5s
wait_time = constant(3)

# constant_pacing(3):保证每 3 秒启动一次新迭代
# 如果任务耗时 2s,只额外等待 1s;如果任务耗时 4s,则不等待直接开始下一轮
wait_time = constant_pacing(3)

什么时候用 constant_pacing

当你需要精确的 RPS 目标时选它。比如目标 RPS=100,有 50 个并发用户,那么每个用户的 pacing 应设为 50÷100 = 0.5 秒。constant_pacing 会自动补偿任务执行时间的波动。

4.3 SequentialTaskSet:顺序执行的任务组

默认情况下任务是随机选择的。如果需要按顺序执行一组操作(如完整的下单流程),用 SequentialTaskSet

python
from locust import HttpUser, SequentialTaskSet, task


class CheckoutFlow(SequentialTaskSet):
    """模拟完整购物流程:浏览 → 加购 → 结算"""

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

    @task
    def add_cart(self):
        self.client.post("/post", json={"action": "add_to_cart"})

    @task
    def pay(self):
        self.client.post("/post", json={"action": "pay"})


class ShoppingUser(HttpUser):
    host = "https://httpbin.org"
    tasks = {CheckoutFlow: 1}
    wait_time = between(2, 5)

SequentialTaskSet 内部的任务按声明顺序依次执行,走完一轮后重新从头开始。它本身也可以被赋予权重,与其他 TaskSet 或普通任务混合使用。

4.4 组织最佳实践

真实项目的压测脚本往往包含数十个接口。推荐的目录结构:

text
loadtests/
├── locustfile.py          # 入口:User 定义 + TaskSet 组合
├── flows/
│   ├── browse.py          # 浏览流程
│   ├── checkout.py        # 下单流程
│   └── admin.py           # 后台操作
└── config/
    └── users.py           # 测试账号池

核心原则:

  • 一个"业务流"封装为一个 TaskSetSequentialTaskSet
  • User 类只负责组合这些流并分配权重;
  • 公共逻辑(登录、清理)放在基类或 Mixin 中复用。

本章小结

  • @task(N)tasks = {method: weight} 两种方式控制任务概率;
  • between 随机停顿最像真人;constant_pacing 能补偿执行时间波动,适合精确 RPS 场景;
  • SequentialTaskSet 强制顺序执行,用于不可乱序的业务流(如先加购后结算);
  • 复杂项目按业务流拆分文件,User 类只做组合。

🧪 随堂测验

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

1. @task(5) 与 @task(1) 的选中概率之比是?

2. constant_pacing(5) 与 constant(5) 的本质区别是什么?

3. 以下哪个场景最适合用 SequentialTaskSet?

4. 目标 RPS 为 200,计划用 100 个并发用户达成,每个用户的 constant_pacing 应设为?

🛠️ 动手实践

  1. 编写一个包含 3 个任务(权重分别为 10%、60%、30%)的用户类,跑 30 秒后在 Web UI 统计表中验证请求比例是否符合预期。
  2. 分别用 constant(2)constant_pacing(2) 各跑一次(目标接口 /delay/1),对比两者的实际 RPS 差异并解释原因。
  3. SequentialTaskSet 实现"注册 → 登录 → 注销"三步流程,并在每步之间加入 1 秒停顿。

完成后进入第 5 章