news 2026/9/9 2:42:35

Locust性能测试框架实战:从脚本编写到分布式压测

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Locust性能测试框架实战:从脚本编写到分布式压测

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.12

Linux发行版如果自带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分到一部分虚拟用户去模拟。

启动步骤:

  1. 在Master节点上启动:
locust -f locustfile.py --master --expect-workers=4
  1. 在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, 10

tick方法每秒被调用一次,返回一个(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 = 1

7.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.com

8. 写在最后的几个建议

我自己实践下来,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跑完整压测,基本能保证真正的压测任务发出去之后不会因为脚本低级错误返工。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 2:42:06

FPGA实现SAD模板匹配的实时目标跟踪方案

1. 这不是“又一个图像处理demo”&#xff0c;而是一套能跑在嵌入式边缘端的实时目标跟踪硬核方案 你有没有遇到过这样的场景&#xff1a;在工业检测产线上&#xff0c;需要实时定位某个特定工件的位置&#xff0c;但光照变化大、背景杂乱、目标有轻微形变&#xff1b;或者在无…

作者头像 李华
网站建设 2026/9/9 2:41:39

中国海洋大学计算机考研复试全流程解析与备考策略

如果你已经走到了“初试结束、等待出分”这个阶段&#xff0c;或者正在以中国海洋大学计算机考研为目标搜集情报&#xff0c;那么这篇经验贴应该能帮到你。 中国海洋大学计算机考研复试&#xff0c;在985院校里属于“准备得越充分、越能拉开差距”的类型。它的复试构成不算花哨…

作者头像 李华
网站建设 2026/9/9 2:39:56

纯C语言手写LSTM循环神经网络:从原理到嵌入式部署实践

简介&#xff1a;一套以C语言实现的递归神经网络&#xff08;LSTM&#xff09;开源代码&#xff0c;面向需要在嵌入式或资源受限环境中使用神经网络进行文本学习与生成的开发者。项目参考Andrej Karpathy的char-rnn思路&#xff0c;改用C语言重写&#xff0c;支持CMake与Meson多…

作者头像 李华
网站建设 2026/9/9 2:39:27

MySQL删除数据后表文件不缩小?InnoDB空间回收与碎片整理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/9 2:39:10

春晚机器人背后:专利护城河与产业突围的底层逻辑

开年看春晚&#xff0c;机器人又成了全场焦点。但作为一个在机器人行业摸爬滚打多年的工程师&#xff0c;我盯着屏幕里那些整齐划一的机械臂和灵巧的双足动作&#xff0c;心里想的不是节目效果&#xff0c;而是另一件事&#xff1a;这背后得有多少专利在“贴身肉搏”。从舞台秀…

作者头像 李华
网站建设 2026/9/9 2:39:09

Git提交规范实战:从Commit Message到原子性提交

1. 提交前的第一道门槛&#xff1a;环境与仓库准备1.1 安装Git与三件套配置很多新手拿到Git的第一步不是写代码&#xff0c;而是被安装和配置劝退。Git的安装本身不复杂&#xff0c;各个系统都有对应方案&#xff1a;Windows推荐直接去官网下载安装包&#xff0c;一路Next就行&…

作者头像 李华