1. 从一次真实压测经历说起:我为什么最终选择了Locust
在接触Locust之前,我所在的项目组做性能测试用的工具是JMeter。按理说JMeter够成熟、资料多、组件丰富,为什么后来我把它换掉了?原因是一次非常典型的接口压测任务。当时业务方要求模拟用户走完某个完整的下单链路:登录、查库存、下单、支付回调,每一步携带一套独立的token,且请求之间存在业务依赖。用JMeter的JSON提取器、BeanShell脚本、正则提取器来拼这套流程,配置起来极其繁琐,一个半小时的调试时间里有四十分钟在跟控件和变量作用域较劲。
后来我尝试用Python写了一套基于requests脚本的压测脚本去顶,脚本倒是灵活了,但并发模型是个灾难。线程池模拟几百个用户,GIL一卡,响应时间曲线全程失真。直到那一次性能测试社区里有人提到Locust这个框架,我花了一个下午熟悉了它的核心接口,晚上就把原本在JMeter里调了小半天的下单链路用几十行Python代码实现了,随后在分布式模式下用8台压测机跑了将近一千万请求,整个过程中的负载控制、数据采集、结果实时查看都做得很舒服。从那时起,Locust就变成了我日常压测工作里的主力工具。
这篇文章想做的事,就是把我从安装到落地、从单机到分布式、从脚本到结果分析这一整套Locust性能测试框架实践经验完整地梳理出来。不论你是刚接触性能测试,还是已经有JMeter基础想换个思路,这套内容都能让你少走几周的弯路。
2. 为什么是Locust:不是JMeter不好,是场景不同
2.1 用代码定义压测场景,比拖拽控件好在哪里
JMeter的图形化界面有自己的优势:零代码起步、组件化设计、适合不太写脚本的测试人员。但当你需要处理复杂业务链路时,图形化界面反而成了束缚。每个取样器之间的连接线、变量作用域、条件控制器嵌套、BeanShell脚本的调试体验,都会让一条稍微复杂的业务链路显得无比笨重。
Locust走的是完全相反的路线:测试场景就是一段普通的Python代码。你在代码里定义用户行为、请求参数、业务依赖,所有Python生态的库都能直接用进去。比如你要生成一批符合特定规则的手机号,直接调用faker库;要做一些签名逻辑,直接import hashlib;要连数据库准备测试数据,直接pymysql连上去即可。这种代码层面的自由度,是图形化工具很难给的。
还有一点非常重要:Locust脚本本质上就是Python代码,可以进入任何代码仓库做版本管理,同事之间review压测脚本就像review普通代码一样。我们项目里压测脚本和业务代码在同一个代码仓库里管理,每次业务接口变更时对应的压测脚本也会同步更新,这个好处在长期维护中体现得非常明显。
2.2 Locust的并发模型为什么“结果更真实”
性能测试工具模拟并发的底层机制,直接决定了测试数据的可信度。JMeter的老版本是基于Java线程的,每个虚拟用户占用一个线程,线程多了以后上下文切换开销很大,所以你会发现JMeter跑大并发时压测机自己的CPU先撑不住了,这其实是线程模型本身的成本。Locust则完全走另一条路,它的并发基础是gevent,也就是基于协程的事件循环模型。
协程和线程的区别可以这样理解:线程就像你去银行办事,每个窗口坐着一个柜员,用户多了你会觉得银行里全是人,空气都发闷——CPU上下文切换开销就是这种“发闷”的感觉;协程则像一个非常高效的柜员,一个人能同时处理多个客户的请求,在哪个客户卡住等待时先去服务下一位。所以Locust可以在一台普通机器上轻松模拟几千个并发用户,每个用户的成本很低,而且产生并发请求的模式更贴近真实场景。
但这不代表Locust没有代价。因为大量操作是异步协程模式,如果你的测试目标本身不支持快速的并发连接,反而会产生比线程模型更多的并发连接压力,所以压测机所在机器的文件描述符限制和端口数量一定得提前调好。这部分我后面会单独拎出来讲。
2.3 什么项目适合选Locust,什么情况还是用JMeter
选工具关键看场景,不是跟风。我自己的判断标准是这样的:
- 业务链路复杂,尤其是请求之间有数据依赖、验证逻辑的,优先Locust。因为代码处理依赖关系比配置面板顺手得多。
- 并发规模大,但对单台压测机的资源占用敏感,优先Locust。协程模型可以让你用更少的机器跑出更高的并发。
- 压测脚本需要持续集成到CI/CD流程里,优先Locust。命令行无头模式、CSV导出、Python库可以直接被测试脚本框架调用。
- 完全图配置、连Python基础都没有的团队,JMeter的门槛可能更低,但如果团队愿意投入一周学习Python基础,后续收益是巨大的。
- 单纯发个静态请求,不需要什么逻辑,两者都行,看你哪个熟。
3. 环境准备:这一步踩过坑的人特别多
3.1 Python版本怎么选,最省心的方案
Locust从2.x开始对Python版本就有明确要求,目前最新版本在Python 3.9到3.12之间运行得最稳定。如果你是为了项目重新部署环境,直接装Python 3.11或3.12即可,这两个版本对gevent等底层库的兼容性做得很好,不会出现编译报错之类的问题。
在Windows下安装Python时,有一个特别容易被忽略的细节:安装过程中务必勾选“Add Python to PATH”选项。很多人装完Python之后打开命令行输入python,跳出一堆来自微软商店的提示,或者提示“python不是内部或外部命令”,十有八九是没勾这个选项。已经踩过坑的可以直接去环境变量里把Python安装目录和Scripts目录手动加到Path中。
macOS下建议用Homebrew安装:
brew install python@3.12Linux发行版如果自带Python但版本偏低,建议从官网下载源码编译安装,切忌直接用系统的包管理器替换掉默认的Python,这会把很多系统工具搞坏。我见过不止一个同事因为强行覆盖了系统的Python版本,导致系统包管理崩溃。正确做法是装一个独立的Python版本,用软链接或者虚拟环境方式管理。
3.2 虚拟环境:不用迟早会后悔
很多性能测试脚本一开始在机器上东一个包西一个包,看起来能跑,但一旦换机器或者后来有人重新搭环境,就是一场灾难。依赖包版本冲突更是折磨人。所以我坚持所有压测项目必须用虚拟环境,这在Python项目里不是可选项,应该是默认项。
创建虚拟环境只需要几条命令:
# 创建项目目录并进入 mkdir locust_load_test && cd locust_load_test # 创建虚拟环境 python3 -m venv venv # 激活虚拟环境 # Windows下 venv\Scripts\activate # macOS/Linux下 source venv/bin/activate激活后命令行前面会多出一个(venv)前缀,后面所有依赖都往这个环境里装,和系统环境彻底隔离,互不污染。
3.3 Locust安装,及相关依赖包的注意事项
激活虚拟环境后,安装Locust本身相当简单,一行命令的事:
pip install locust这条命令会自动安装locust及其核心依赖,包括gevent、geventhttpclient、flask、werkzeug、pyzmq、psutil等。其中pyzmq主要用于分布式模式下Master和Worker之间的通信,如果你的Python环境没有预装编译好的wheel包,这条命令可能需要编译安装,那就会慢一些,甚至报错。遇到这种情况,解决方案是先升级一下pip和setuptools:
pip install --upgrade pip setuptools wheel然后再执行pip install locust。如果还是编译报错,可以分步安装,先把依赖逐一装好:
pip install gevent geventhttpclient pyzmq psutil pip install locust安装完成后验证一下版本:
locust --version能输出版本号就说明环境已经就绪了。我平时一般会顺手装一个requests库,虽然locust自带HttpUser的client本质上就是封装了requests的功能,但有些辅助脚本、数据准备脚本里还是直接import requests更方便。
4. 第一个Locust压测脚本:彻底吃透核心概念
4.1 最小可运行脚本拆解
从零开始,先看一个最简单的Locust脚本。它模拟用户访问一个网站首页,每两个用户动作之间随机等待1到5秒:
from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time = between(1, 5) @task def load_homepage(self): self.client.get("/")这段代码只有三个关键元素:
- HttpUser类:代表一类虚拟用户。Locust每创建一个并发用户,就相当于实例化一个HttpUser对象。HttpUser除了继承自User的所有能力之外,自带一个client属性,这个client可以用get、post、put、delete等方法发出真实HTTP请求。
- wait_time:定义用户执行完一个任务后,在开始下一个任务之前的等待时间。between(1, 5)表示每次等待的时间在1到5秒之间随机取值,模拟真实用户的操作间隔。
- @task装饰器:把下面这个函数标记为“用户会执行的任务”。不加这个装饰器,函数就只是普通方法,不会被Locust执行。
启动这个脚本:
locust -f locustfile.py --host=https://example.com然后浏览器访问http://localhost:8089,就能看到Locust自带的Web UI。界面里有几个输入框,分别用来填并发用户数、每秒启动的用户数、压测的Host(如果命令行已经指定了,这里可以留空),点“Start swarming”按钮即可开始压测。
4.2 任务、权重和TaskSet:怎么模拟用户行为才能更像真人
真实用户的行为不是只会点一个页面的。用户可能先看首页,然后点进商品列表,再打开商品详情,最后偶尔提交一个订单。Locust通过两种方式组织这种多任务行为。
方式一:直接在User类里堆多个@task方法。每个任务可以给个权重值,括号里的数字越大,被选中的概率越高。
from locust import HttpUser, task, between class StoreUser(HttpUser): wait_time = between(0.5, 3) @task(10) def view_home(self): self.client.get("/") @task(5) def view_products(self): self.client.get("/products") @task(2) def view_detail(self): self.client.get("/products/1001") @task(1) def add_to_cart(self): self.client.post("/cart/add", json={"product_id": 1001, "count": 1})权重值10/5/2/1意味着:在100次任务选择中,view_home大约会被执行55次,view_products约28次,view_detail约11次,add_to_cart约6次。这个比例关系完全由你来决定,等于把你对业务频率的认知直接写进压测脚本。
方式二:使用TaskSet。当用户的行为序列比较复杂,比如必须先登录、再进入某个业务子流程时,可以把一组任务封装到TaskSet里,再通过@task关联到User上:
from locust import HttpUser, task, between, TaskSet class PaymentFlow(TaskSet): @task(1) def pay(self): order_id = 9527 self.client.post("/api/pay", json={"order_id": order_id}) @task(3) def cancel(self): self.client.post("/api/cancel", json={"order_id": 9527}) class CustomerUser(HttpUser): wait_time = between(1, 3) @task class UserPaymentFlow(PaymentFlow): pass @task(2) def view_index(self): self.client.get("/")TaskSet可以在类内嵌套,控制一组任务的进入和退出。比如可以在TaskSet的on_start里准备前置数据,在on_stop里做清理工作。如果你的业务链路固定、顺序明确,用TaskSet来组织会比散落的@task更清晰。
4.3 on_start和on_stop:每个用户上线前和下线前做什么
Locust里还有一个非常重要的生命周期方法。每个虚拟用户从启动到销毁,会依次经过这几个阶段:实例化、调用on_start、循环执行任务列表、用户停止时调用on_stop。
on_start最常见的用途就是登录。因为真实用户访问系统之前必然先做身份认证,压测里的每个虚拟用户也应该携带自己的登录态去发起请求,这样服务端的鉴权逻辑、Session管理等环节才会被真实地压到。
from locust import HttpUser, task, between class ShopUser(HttpUser): wait_time = between(1, 5) def on_start(self): resp = self.client.post("/api/login", json={ "username": "load_test_user", "password": "123456" }) # 把token保存到实例属性,后续请求都可以用 self.token = resp.json().get("token") self.headers = {"Authorization": f"Bearer {self.token}"} @task def get_orders(self): self.client.get("/api/orders", headers=self.headers)需要注意,self.client这个对象在并发模式下是被复用的,所以每个用户在on_start里保存的self.token是用户实例级别,不会串号。这一点和JMeter里“每个线程共享变量池”的惯性思维很不一样。
4.4 wait_time的几种策略,别只盯着between
- between(min, max):每次任务后随机取min到max之间的等待时间。最常用。
- constant(seconds):固定间隔,适用于某些定时轮询类的任务。
- constant_pacing(seconds):保证每个任务的总耗时固定。如果任务本身运行了2秒,而constant_pacing设置为5秒,那就会再等3秒;如果任务跑了6秒,Locust不会额外等待,直接进入下一个任务。这种策略适合模拟“每秒固定调用N次”的接口。
- 自定义函数:直接传一个无参函数,每次返回一个等待秒数即可。
我个人的经验是:对于普通Web应用压测,between是最稳妥的。它给系统制造了一定的随机性,不会像constant那样产生“同步冲击”效应。真实用户访问系统时不可能整齐划一地隔2秒就点一次,这一点随机性对测试结果真实性影响很大。
5. 进阶场景实战:登录态、参数化和分布式部署
5.1 高并发登录压测的完整方案
接口需要登录才能访问,压测时既要保证每个虚拟用户拥有自己的身份凭证,又不能让登录这一步占用过多资源。比较常见的设计是:在一开始用一个种子账号池批量换取token,然后把这些token轮询分配给虚拟用户。
一个可以直接用的思路是:在压测开始之前,先用脚本生成一批有效的账号登录态,保存到内存列表里,每个虚拟用户启动时从列表里取一个(加锁或使用线程安全方式),这样就不会在压测开始时因为所有人同时登录导致认证服务先崩掉:
import json import threading from locust import HttpUser, task, between TOKEN_POOL = [] LOCK = threading.Lock() def load_tokens(): with open("tokens.json", "r", encoding="utf-8") as f: TOKEN_POOL.extend(json.load(f)) class ApiUser(HttpUser): wait_time = between(0.1, 0.5) def on_start(self): with LOCK: if TOKEN_POOL: self.token_info = TOKEN_POOL.pop() else: self.token_info = None # 如果token不够,动态补充 if self.token_info is None: resp = self.client.post("/api/login", json={ "username": "perf_user_extra", "password": "123456" }) self.token_info = {"token": resp.json().get("token")}这个方式把“登录态准备”和“业务压测”解耦,压测启动后不会出现登录风暴。如果压测规模较小、秒级并发不高,也可以直接在on_start里每人独立登录,省去预处理流程。
5.2 请求参数化:避免数据集中让系统“提前扛不住”
压测如果每次都请求同一个商品ID或者同一个用户账号,系统的缓存机制很可能让某个热点数据扛住大部分流量,掩盖了真正的性能瓶颈。所以参数化是必须的。
参数化思路很简单:把测试数据提前准备好,比如从数据库导出真实的商品ID列表,或者用代码生成一批符合业务规则的唯一标识,然后在请求时随机取用:
import random from locust import HttpUser, task, between PRODUCT_IDS = [random.randint(100000, 999999) for _ in range(5000)] class QueryUser(HttpUser): wait_time = between(0.2, 1) @task def query_product(self): pid = random.choice(PRODUCT_IDS) self.client.get(f"/api/product/{pid}")如果业务上要求参数不重复,或者对参数格式有严格校验,可以用队列结构配合workers之间共享。Locust分布式模式下,各Worker进程之间内存不共享,如果希望每个请求拿到的参数不重复,简单的办法是提前把参数文件切分成多份,每个Worker用不同的偏移量去读取。
5.3 分布式压测:当单机并发上不去时的标准解法
单机可以模拟的最大并发数是有限制的,主要受限于压测机自身的CPU、内存、端口资源和文件描述符限制。当单机无法继续增加用户数时,就需要横向扩展压测机,让多台机器协同工作。
Locust的分布式架构很清晰:一台Master节点负责控制调度和汇总结果,若干台Worker节点真正执行压测任务。用户数由Master统一分配,每个Worker分到一部分虚拟用户去模拟。
启动步骤:
- 在Master节点上启动:
locust -f locustfile.py --master --expect-workers=4- 在4台Worker节点上分别启动:
locust -f locustfile.py --worker --master-host=192.168.1.10几个关键参数说明:
- --master:以Master模式启动。
- --worker:以Worker模式启动。
- --master-host:指定Master机器的IP地址,如果端口不是默认的5557,还需要加--master-port参数。
- --expect-workers=4:告诉Master预期连接4个Worker。加上这个参数后,当所有Worker就绪,Master才会开放Web UI并允许启动压测,这样可以避免压测启动时只有部分Worker在跑,导致负载不均衡。
- --processes=4:如果你只有一台多核机器,也可以在本机用这个参数起多个进程模拟分布式效果。这实际上是在单机内部起了一个Master和多个Worker,对利用多核CPU很有帮助。
分布式模式跑起来之后,Web UI和单机版几乎没有差别,所有结果数据会汇总到Master界面显示。但要注意:Worker节点的系统时钟最好和Master保持同步,否则压力爬坡时不同节点产生请求的节奏会有偏差。
5.4 自定义负载策略:让并发数按照预想的方式变化
默认情况下,通过Web UI启动压测时,并发用户数会按照你填的spawn rate爬升,然后保持在设定值不变。但实际业务场景往往不是这样的。比如你需要模拟“上午10点突然有大量用户涌入”这种尖峰流量,或者“每隔5分钟出现一波小高峰”的周期性流量。
Locust提供了LoadTestShape基类,允许你用代码控制并发用户数随时间的动态变化:
from locust import LoadTestShape class SpikeShape(LoadTestShape): time_limit = 600 spike_time = 120 base_users = 50 spike_users = 500 def tick(self): run_time = self.get_run_time() if run_time > self.time_limit: return None if run_time % 240 < self.spike_time: return self.spike_users, 100 return self.base_users, 10tick方法每秒被调用一次,返回一个(current_user_count, spawn_rate)元组,分别表示当前应该维持的并发用户数和用户增长速度。返回None表示测试结束。上面这段代码的意思是:每240秒周期里,前120秒模拟500个并发用户(每秒增加100个),后120秒回落到50个并发用户(每秒只增加10个)。通过自定义tick逻辑,几乎可以模拟任意形状的负载。
6. 压测结果怎么看:关键指标与性能瓶颈定位
6.1 Web UI里那些数字到底是什么意思
Locust的Web UI默认监听8089端口,启动压测后页面上会实时展示一系列指标。很多刚开始接触Locust的人只看一个“响应时间平均值”,实际上有几个指标更值得关注:
- Requests/s(RPS):每秒请求数,这是吞吐量的直接体现。这个数字往上走不一定是好事,要看它和响应时间的关系。如果RPS没涨而响应时间涨了,说明系统已经接近处理极限。
- 平均响应时间(Average):所有请求响应时间的算术平均。这个数字受极端值影响比较大,只能作为参考。
- 50% / 90% / 99% 分位响应时间:分别表示有50%、90%、99%的请求响应时间小于等于这个值。99%分位这个指标比平均值重要得多,因为它直接反映最差那批用户的体验。如果p99响应时间一直很高,说明系统存在明显的长尾延迟。
- 失败率(Failures):请求失败的比例。压测中如果出现极少量5xx错误其实正常,但失败率突然高于0.1%就要立刻排查。
- 当前在线用户数(Current Users):实时并发用户数,应与设定值一致。
Web UI下方还有两个图,一个展示Request Stats,包括不同接口的请求量、响应时间、失败数;另一个展示响应时间分布曲线,能看到是否存在明显的响应时间分叉。
6.2 无头模式和CSV数据落盘:CI/CD里的正确姿势
在命令行环境里跑压测,尤其是在持续集成流水线里自动化执行,不能依赖Web UI人工操作。这时用无头模式配合CSV输出就是标配:
locust -f locustfile.py --headless -u 1000 -r 100 -t 15m --csv=result --host=https://api.example.com参数含义:
- --headless:不启动Web UI。
- -u 1000:模拟1000个并发用户。
- -r 100:每秒增加100个用户。
- -t 15m:压测持续15分钟。
- --csv=result:把统计信息保存到以result开头的CSV文件中。
运行结束后,会生成多个CSV文件。其中result_stats.csv包含不同接口的聚合统计信息,result_stats_history.csv记录每一秒的实时指标变化,result_failures.csv记录失败情况。拿到这些文件之后,可以写一段Python脚本解析并生成HTML报告,也可以导入到Grafana里做可视化,还可以存成测试基准用于后续版本对比。
6.3 从数据到结论:快速定位性能瓶颈的思路
结果数据出来之后,最忌讳的就是只报一个“平均响应时间200ms”就完事。好的性能测试结论至少要回答几个问题:系统容量上限在哪?瓶颈在哪个环节?
我的分析习惯是三步走:
第一步,对比RPS和响应时间曲线。如果随着并发用户数上升,RPS同步上升而响应时间保持平稳,系统还在健康区间;如果RPS开始停滞,响应时间却快速上涨,证明系统已经打满,需要寻找瓶颈。
第二步,看p99和p95之间的差距。如果p99远远高于p95,说明有一小部分请求明显变慢,很可能是数据库连接池耗尽、缓存穿透、垃圾回收停顿等典型的“偶发慢请求”问题。
第三步,到服务端做分层验证。出现瓶颈时,同时看服务端的CPU、内存、磁盘IO、网络IO这些指标。如果服务端CPU低而数据库CPU高,说明瓶颈在数据库层;如果服务端CPU高但响应时间还是上不去,先在应用的线程池和日志输出上找问题。Locust提供的数据告诉你现象,而瓶颈定位还需要结合监控系统做交叉验证。
7. 常见问题排查:这些坑我基本都踩过一遍
7.1 压测机报Connection reset by peer
压测初期最容易遇到的问题。如果确认目标服务安全组规则没问题、后端服务也没崩,那大概率是压测机自己的连接数限制或者端口耗尽。Linux下可以临时调大相关限制:
# 查看当前限制 ulimit -n # 临时调大(修改后当前会话生效) ulimit -n 65535如果需要永久生效,编辑/etc/security/limits.conf,添加:
* soft nofile 65535 * hard nofile 65535同时还要检查TCP端口是否耗尽:
net.ipv4.ip_local_port_range = 1024 65535 net.ipv4.tcp_tw_reuse = 17.2 并发用户数上去后,压测机CPU先打满了
协程模型虽然省资源,但压测机自身的CPU也不是无限的。如果压测机CPU打到90%以上,说明这台机器的压力生成能力已经到达上限。解决办法就是加Worker节点,用分布式扩展,而不是继续堆用户数。如果一台机器跑500用户CPU就满了,再往这台机器上加用户只会让用户的实际请求频率下降,测试数据完全失真。
7.3 on_start登录失败导致所有用户跑不起来
这个坑在压测有鉴权接口时经常遇到。如果on_start里登录失败抛了异常,Locust会认为这个用户初始化失败,不会去执行后续任务,现象就是启动后Web UI显示用户数量上去了但RPS一直是0。
排查办法很简单:在on_start里不要直接让异常向上抛,而是捕获并记录日志:
def on_start(self): resp = self.client.post("/api/login", json={...}) if resp.status_code == 200: self.token = resp.json().get("token") else: self.environment.runner.stop() logging.error("Login failed: %s %s", resp.status_code, resp.text)登录失败时主动停止压测,这样能在第一时间发现问题,而不是看着一片0请求干着急。
7.4 结果波动极大,几次压测数据对不上
如果同一份压测脚本、同样的并发数,几次跑出来的结果差异很大,先别怀疑系统有玄学问题。常见原因有三个:一是测试数据没隔离干净,前一次压测写入的数据影响了后一次的性能;二是压测时段不同,业务系统在高峰期之外可能还有其他定时任务在占用资源;三是压测机本身性能不稳定,比如笔记本降频、云主机被其他租户抢占CPU。
比较保险的做法是:每次压测前清理测试数据,选择业务低峰期执行,压测机用固定规格的服务器而非共享型机器,并在压测前后记录压测机的CPU和内存基线,作为数据可信度的佐证。
7.5 结果保存CSV文件后,才发现漏了细节
CSV文件默认记录的是聚合数据,如果想看每一秒的动态数据,需要在启动时加参数:
locust -f locustfile.py --headless -u 100 -r 10 -t 5m --csv=result --csv-full-history加了--csv-full-history之后,result_stats_history.csv会记录每秒的详细数据,画趋势图、做性能对比时能派上大用场。另外建议每次压测给CSV文件名带个时间戳,避免覆盖旧结果:
locust -f locustfile.py --headless -u 100 -r 10 -t 5m --csv=result_$(date +%Y%m%d_%H%M%S) --host=https://api.example.com8. 写在最后的几个建议
我自己实践下来,Locust最大的价值不是它比JMeter快多少,而是它把性能测试脚本变成了一种可以持续演进、可以代码审查、可以自动化集成的工程资产。一个性能测试团队如果只是在发版前临时压一压,那用什么工具都差不多;但如果想把性能测试沉淀为长期机制,我推荐认真学一下Locust。
最后分享一个我觉得特别实用的小技巧:给Locust脚本写一个简单的“冒烟测试”模式。正常压测脚本会跑很长时间,不适合每次代码改动都执行。可以在脚本里加一个环境变量,当设置SMOKE=1时,把wait_time修改为0,并发数由命令行传入很小的值,执行时间缩短到几十秒:
import os class ApiUser(HttpUser): if os.environ.get("SMOKE") == "1": wait_time = constant(0) else: wait_time = between(0.5, 2)这样本地开发时、每次接口变更后,都能在几分钟内快速验证一遍脚本是否还正常,而不必等待完整压测周期。配合CI跑完整压测,基本能保证真正的压测任务发出去之后不会因为脚本低级错误返工。