☰
Locust性能测试实战:协程并发与分布式压测指南
2026/10/20 18:36:14 网站建设 项目流程

我先给你把话放这儿:Locust 绝对不是那个“动物园里最猛的单测工具”,它是个正经的、用纯 Python 写压测脚本的性能测试框架。如果你所在的团队正在用 JMeter 用得想砸键盘,或者你刚接手一个需要摸清线上容量底线的项目,那么花一个下午掌握 Locust,绝对是你今年做过最值当的技术投资。

先说个我自己的真实感受。早期我做性能测试,用的全是图形化工具。遇到复杂业务场景,得拖一堆取样器,配一堆正则表达式,脚本一多根本维护不动。后来接触到 Locust,第一次发现原来压测还能像写业务代码一样干净:一个 Python 文件,定义用户行为,跑起来就是上千并发。这种感觉就像你开惯了手动挡,突然换成了自动挡,虽然能折腾的地方少了,但人车合一的感觉更强了。而且因为脚本就是代码,可以进 Git,可以做 Code Review,可以复用公共方法,这在整个性能测试工具链里算是降维打击。

这篇文章我不会跟你扯太虚的概念。我会从为什么选它、环境搭建、核心概念、实战脚本、分布式压测、结果分析到常见坑位,一步步带你走完。不管你是第一次接触压测的新手,还是从其他工具转过来的老手,看完这篇基本能上手负责真实的压测任务了。

1. 为什么偏偏是 Locust:拿它和传统压测工具硬碰硬

1.1 脚本即代码,这是本质区别

传统的压测工具,无论是开源的还是商业的,核心思路都是通过 GUI 操作来构建测试计划。取样器、监听器、逻辑控制器,这一整套东西本身就是一套复杂的“新语言”。你得学它的组件体系,学它的作用域规则,学它的变量传递方式。学完这些之后,你发现还得处理各种格式的断言、提取器,效率其实很低。

Locust 不一样。它把压测场景的抽象层直接建立在你已有的 Python 技能树上。一个压测脚本就是一个普通的 Python 文件,里面定义了一个继承自 HttpUser 的类,类里写几个用 @task 装饰器修饰的方法,每个方法就是用户在系统里的一次操作。这种模式有几大不可替代的优势:

  • 业务逻辑完全代码化,可以做函数封装。比如登录、下单、支付这些动作,你可以写成公共方法,在不同的场景里复用。
  • 参数化、断言、数据校验,全部用 Python 原生的方式解决,不需要学各种“组件语言”。
  • 代码可以进版本库、可以做代码评审、可以写单测,协作方式跟开发流程完全对齐,这在大型团队里非常重要。

1.2 协程模型 vs 线程模型:并发能力的底层逻辑

JMeter 的每个虚拟用户背后是一个独立的 Java 线程。Java 线程是操作系统级别的资源,创建、切换、销毁都有不小的开销。你开 1000 个并发,机器上可能就多了 1000 多个线程上下文切换,CPU 光忙着切换线程了,反而没有余力去发送请求。

Locust 的底层模型是 gevent,也就是基于协程的并发模型。协程的切换是在用户态完成的,开销比线程切换小一两个数量级。所以一台普通的压测机用 Locust 模拟几千并发是很轻松的事,资源占用极低。你甚至可以在一台低配的云主机上跑出 JMeter 集群才能达到的压力水平,省下的都是实打实的机器成本。

我用一个生活化的类比解释一下:线程模型就像你雇了 1000 个员工,每个人守一部电话,等着对方接听;协程模型就像你雇了 1 个接线员,同时盯着 1000 部电话,哪部有动静就接哪部,没动静的继续等着。人员成本自然不可同日而语。

1.3 实时指标和 Web 控制面板:压测过程一目了然

Locust 自带一个基于 Web 的监控界面。启动压测后,你在浏览器里打开默认的 8089 端口,可以看到实时变化的请求数、每秒响应数、响应时间分布、失败率、用户并发数等关键指标。它还有一个图表页面,会把整个压测过程中每个指标的曲线绘制出来,非常直观。

这个设计极大降低了压测的“黑盒感”。压测过程中你随时能知道系统处于什么状态,到底是请求发不出去,还是服务端处理不过来,从曲线里能看出个大概。在调优阶段甚至可以一边调整参数一边观察曲线的实时变化,体验比看命令行日志好出几个量级。

2. 环境搭建与第一个压测脚本:先跑起来再说

2.1 安装:一行命令的事

前提是你机器上有 Python 3.8 以上的环境。这一步非常简单,直接用 pip 安装:

pip install locust

装上之后验证一下版本,确认安装成功:

locust --version

如果不出意外,你会看到类似 2.x 的版本号。需要留意的是,Locust 迭代速度很快,不同版本之间 API 有细微差异。建议你直接学最新的稳定版,网上老的教程多数基于 0.x 或 1.x,其中有些 API 已经过时了,如果你的项目时间充裕,也可以多翻翻官方文档的更新日志。

2.2 编写第一个压测脚本:模拟用户打开首页

新建一个文件locustfile.py,这是 Locust 默认会寻找的脚本名。内容先写得极简,感受一下整体结构:

from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time = between(1, 5) @task def load_homepage(self): self.client.get("/")

就这么三段话,一个可运行的压测脚本已经完成了。解释一下每一部分:

  • class WebsiteUser(HttpUser):定义一个虚拟用户类。每个并发用户就是 HttpUser 类的一个实例。
  • wait_time = between(1, 5):每个用户执行完一次任务之后,会随机等待 1 到 5 秒再进行下一次请求。这用来模拟真实用户的思考时间。
  • @task装饰器:把这个方法标记为用户的一个操作步骤。

self.client是 Locust 为用户自动创建的 HTTP 客户端,它本质上是requests.Session的一层包装。这意味着它会自动管理 Cookie,会记录每个请求的响应时间,会统计成功与失败的状态码。你不需要手动处理这些细节,框架内部已经帮你做了。

2.3 运行模式:Web 模式与无头模式

在命令行中直接运行locust,默认会启动 Web 界面模式。

locust -f locustfile.py

然后浏览器访问http://localhost:8089。你会看到一个表单,需要填写两个数字:总用户数(Total users)和每秒增速(Spawn rate)。打个比方:如果你填了用户数 100、增速 10,那么 Locust 会在一秒里创建 10 个虚拟用户,10 秒内全部上线,之后保持 100 个并发不断发请求。

如果你希望压测过程中不需要人工操作,直接命令行压完出结果,那就用无头模式:

locust -f locustfile.py --headless -u 100 -r 10 --run-time 5m
  • -u 100:总并发用户数 100。
  • -r 10:每秒启动 10 个用户。
  • --run-time 5m:压测持续 5 分钟之后自动停止。

无头模式适合放在 CI/CD 流水线里做自动化回归,也适合半夜里没人盯着跑的长时间稳定性压测。

3. 核心知识点逐个拆解:把每一个概念吃透

3.1 用户的权重与任务比例:真实业务不能一刀切

真实业务环境里,用户的访问行为不可能一刀切。比如一个电商系统,可能 60% 的用户在浏览商品,30% 在加购物车,10% 在下单支付。这些动作对服务器的压力完全不同,无法用同一个频率去模拟。

Locust 提供了非常直白的解决方案:多个 @task 装饰器叠加权重值。

class WebsiteUser(HttpUser): wait_time = between(1, 3) @task(6) def view_items(self): self.client.get("/items") @task(3) def add_to_cart(self): self.client.post("/cart", json={"item_id": 1}) @task(1) def checkout(self): self.client.post("/order", json={"item_id": 1})

这里的权重表示:在每次任务轮转时,view_items被选中的概率是 6/10,add_to_cart是 3/10,checkout是 1/10。这个机制能非常逼真地模拟出真实流量分布,是压测脚本设计中最需要动脑子的环节。

注意:@task 的权重是决定用户“下一步做什么”的概率,而不是决定用户比例的绝对数量。比如你有 100 个并发用户,权重是 6:3:1,那么大致相当于有 60 个用户在做浏览动作、30 个在加购物车、10 个在下单。这里用的是概率模型,因为是随机选择,所以实际分布会在统计意义上接近权重比,这是在压测中比较常见的理解误区。

3.2 任务间的等待时间:模拟真实用户的思考节奏

wait_time不只是休息那么简单。如果所有用户都不停歇地发请求,那是在做极限压力测试,测出来的是服务器的绝对上限。但真实用户总会浏览一下、想一下、填个表单,这个操作间隔就是等待时间。

Locust 提供了几种内置策略:

  • constant(3):固定等待 3 秒。适合模拟自动化程序或第三方回调。
  • between(1, 5):在 1 到 5 秒之间随机取值,符合大多数真实用户的行为曲线。
  • constant_throughput(10):保证每秒每个用户发起的请求量在一个恒定速率。这个适合做匀速流量控制。

写个具体例子。你有一个登录接口,想模拟 20 个用户以每秒 2 次的频率去登录,那就可以:

class LoginUser(HttpUser): wait_time = constant_throughput(2) @task def login(self): self.client.post("/api/login", json={"user": "demo", "pwd": "123456"})

但要注意,constant_throughput实现的是用户级别的匀速,它在任务完成时间极短的情况下才会接近设定值。如果你的接口响应时间忽高忽低,实际请求速率也会有波动。这是由协程调度机制决定的,属于正常现象。

3.3 HttpUser 的 client 对象:本质是 requests.Session

很多新手以为self.client就是普通的 requests 库,其实它们之间有微妙的差异。self.client是 Locust 封装的HttpSession,它保留了requests.Session的大部分特性,比如连接池复用、Cookie 持久化、SSL 证书验证等,但它在每次请求时会自动记录耗时、成功/失败状态,并且这些数据会汇总到控制面板的统计指标中。

这意味着你写压测代码和写普通的业务代码没有本质区别。比如登录后需要带 Token 访问后续接口:

class UserBehavior(TaskSet): token = "" def on_start(self): resp = self.client.post("/api/login", json={"user": "demo", "pwd": "123456"}) self.token = resp.json()["token"] @task def get_profile(self): self.client.get("/api/profile", headers={"Authorization": f"Bearer {self.token}"})

注意on_start方法。每个虚拟用户开始运行时都会先执行这个钩子方法,非常适合做初始化和前置数据准备。它对应的是 JMeter 里的“Setup Thread Group”,但表达上自然得多。

3.4 TaskSet:让复杂的业务流程有结构感

像登录、浏览、下单、支付这类多步骤业务,如果全部平铺在一个 User 类里,很快类就会变得巨大无比。Locust 提供了TaskSet来拆分业务流程,让代码的组织方式更接近真实的模块化开发。

假设有一个购物的完整流程:

class ShopFlow(TaskSet): @task(1) def login(self): self.client.post("/api/login", json={"user": "demo", "pwd": "123456"}) @task(2) def browse(self): self.client.get("/api/shop/list") @task(1) def pay(self): self.client.post("/api/pay", json={"order_id": "12345"}) class WebsiteUser(HttpUser): tasks = [ShopFlow] wait_time = between(1, 3)

TaskSet 还能嵌套,比如在订单 TaskSet 里再挂一个售后 TaskSet。这种组织方式特别适合复杂业务链路,而且代码可以复用——你定义好的 TaskSet,既可以让普通的 HttpUser 执行,也可以挂到其他 TaskSet 下面,灵活度非常高。

3.5 自定义负载形状:突破固定并发模式的限制

wait_time和固定并发数可以解决大多数场景,但有些压测需要更精细的流量控制。比如模拟典型互联网流量的“锯齿形”脉冲波动:早高峰流量逐步上升,午休平缓,晚高峰二次冲顶。这时候固定并发模式就不够用了,需要自定义负载形状。

Locust 提供了一个LoadTestShape类,通过重写tick方法返回当前应该达到的用户数:

from locust import LoadTestShape class WaveShape(LoadTestShape): time_limit = 600 spawn_rate = 20 def tick(self): run_time = self.get_run_time() if run_time < 120: return 50, self.spawn_rate elif run_time < 240: return 200, self.spawn_rate elif run_time < 360: return 100, self.spawn_rate elif run_time < 480: return 300, self.spawn_rate return None

tick方法每个时刻都会被调用。返回的元组是(用户数, 每秒增长数)。返回None则压测停止。这种方式能实现极其复杂的流量模型,把容量评估做得足够真实可信。我在实际项目中就是用这种方式去模拟某些业务突发高峰,得到的容量预估数据比固定并发法准很多。

4. 实战压测场景:从简单到复杂的三种典型脚本

4.1 单接口基准压测:先摸清硬件的底

拿到一个系统,第一件事往往是对核心接口做基准压测,摸清它在理想状态下的最大 TPS 和延迟分布。

from locust import HttpUser, task, between class BenchmarkUser(HttpUser): wait_time = constant(0) # 不等待,极限压 @task def query_order(self): self.client.get("/api/order?id=2001")

不等待 + 固定并发用户数逐步递增,这是基准测试的经典操作。比如先用 50 并发跑 5 分钟,再用 100 并发跑 5 分钟,观察 TPS 是否随并发数线性增长。如果增长到一个阈值后不再上升,基本可以判断服务端或者数据库的某个资源已经到瓶颈了。

注意:constant(0)会让用户每完成一次请求后立即发起下一次请求,这实际上是在做最大努力压测。这种模式一定要配合监控系统观察被压测服务器的 CPU、内存、磁盘和网络,否则压力过大可能直接把服务打挂,影响线上真实流量。

4.2 场景化压测:多用户混合操作的真实世界

从基准测试出来,就要做混合场景压测,也就是把前面说的任务权重、思考时间都用上。下面这个脚本是典型的场景压测结构:

from locust import HttpUser, task, between class MixedUser(HttpUser): wait_time = between(1, 3) def on_start(self): self.client.post("/api/login", json={"user": "demo", "pwd": "123456"}) @task(5) def load_home(self): self.client.get("/") @task(3) def view_detail(self): self.client.get("/api/detail?id=100") @task(2) def submit_form(self): self.client.post("/api/feedback", json={"content": "nice post"})

这里的on_start先模拟了登录动作,之后每个用户的行为都是“登录后的真实用户”。这种方式比直接压接口更能反映真实线上负载模式。

混合场景压测的难点在于各接口的权重比例怎么定。一般拿线上 Nginx 或网关的访问日志做统计,按实际的请求量占比去配置权重。没有日志的话,可以跟产品或运营要业务预估数据,拼出一个大家都认可的比例。

4.3 参数化与数据唯一性:避免缓存假象

压测里有一个超级隐蔽的坑:如果你的请求永远是同一份数据,服务端和数据库都缓存了,那么压出来的性能数据会虚高,完全不能反映真实情况。

举个具体场景,你要压测“根据用户 ID 查询订单列表”这个接口。如果你永远用user_id=1001去查,那么经过一段时间的压测,这条数据的查询计划、结果集、甚至页面模板都可能在缓存层里热起来。真实用户不可能只查同一个 ID,所以压出来的结果远低于实际容量。

解决办法是做参数化。用 Python 生成有序或者随机的数据:

import random from locust import HttpUser, task, between class QueryUser(HttpUser): wait_time = between(1, 2) @task def query_user_order(self): user_id = random.randint(10000, 99999) self.client.get(f"/api/user/{user_id}/orders")

这样每个请求都汇入不同的用户数据,缓存热点的干扰被消除了,压测结果才趋近真实。

还有一种更贴近真实的做法:先从数据库或接口导出真实数据清单,放到一个文件里,脚本运行时随机抽取:

import random from locust import HttpUser, task, between with open("user_ids.txt") as f: user_ids = [line.strip() for line in f if line.strip()] class RealDataUser(HttpUser): wait_time = between(1, 2) @task def query_user_order(self): user_id = random.choice(user_ids) self.client.get(f"/api/user/{user_id}/orders")

这段代码在模块加载时把数据全部读入内存,每次请求随机抽取一个。注意user_ids文件需要在你启动 Locust 的目录下。如果数据量很大,也可以用itertools.cycle做循环读取,但要注意防止内存占用过大。

4.4 分布式压测:单机不够,就上集群

单台压测机用协程可以撑住大几千的并发,但如果你的目标是更高,或者希望模拟来自不同地域、不同 IP 的流量,就得考虑多台机器组成压测集群。

Locust 的集群模式非常清爽,分为一个 master 和多个 worker:

# 在压测主控机器上 locust -f locustfile.py --master --expect-workers=4 # 在 4 台压测执行机器上分别执行 locust -f locustfile.py --worker --master-host=<主控机器IP>

执行之后,master 负责调度和汇总统计,worker 各自负责创建虚拟用户并发送请求。整个集群的并发能力就变为所有 worker 机器能力之和。

分布式压测有一个很容易忽略的注意点:worker 机器必须有一份完全相同的脚本文件和依赖数据。如果脚本引用了外部文件,这个文件也得同步到每一台 worker 上,否则 worker 启动时会直接报错或者行为不一致。

另外,如果多台压测机之间网络延迟太大,也可能导致 master 汇总的指标有偏差。所以压测机尽量和被测系统部署在同一内网或同一地域的云环境,避免把网络延迟混入业务响应时间。

5. 数据指标详解与性能瓶颈定位:怎么看懂压测结果

5.1 Web 界面的实时指标:每个数字都在透露信息

跑起来之后,Web 界面会持续刷新统计数据。核心指标如下:

  • Requests:已经发出的总请求数。
  • Failures:失败的请求数。这里不仅包括 HTTP 5xx 错误,还包括连接超时、读超时等传输层异常。
  • Median / Average:响应时间的中位数与平均值。中位数比较能反映大多数用户的真实体验,平均会被长尾请求拉高。
  • Average size:平均每个响应体的大小,可以用于排查是否存在大量重复数据传输。
  • Current RPS:当前每秒请求数,这是吞吐量的直接体现。
  • Failure:失败请求的具体错误类型和数量,比如连接超时、HTTP 500 等。

这里面最有价值的视角是关注中位数和95% 分位响应时间。平均值哪怕很好看,也可能被少量极大值抬高。95% 分位才是大多数用户感受的上限,任何一个性能报告里都少不了这个数字。

强烈建议在压测的同时拉上被压服务器的监控数据,比如 CPU、内存、磁盘 IO、网络带宽。单纯看压测客户端的数据只能知道“服务变慢了”,但看不出来“是 CPU 打满了还是数据库慢了还是带宽不够”,必须两头数据对照着看才能定位瓶颈。

5.2 CSV 报告导出:保存现场,复盘有据

压测完之后,最好把数据导出成 CSV,留档备查。Web 模式下,在界面上就能直接下载三个维度的数据:请求统计、失败请求、异常日志。

命令行模式下,加几个参数:

locust -f locustfile.py --headless -u 100 -r 10 --run-time 10m --csv=full_result

运行结束后,当前目录下会生成full_result_stats.csv、full_result_failures.csv和full_result_history.csv等文件。把这几份文件归档起来,后续不管是写压测报告还是做前后对比,都有现成的数据基础。

后面如果想用代码对 CSV 做二次分析,也完全没问题。你用 pandas 读入文件,按时间维度绘制 TPS 和响应时间的趋势图,可以得到更丰富的分析结果。

5.3 定位瓶颈的实战思路:从现象到根因

压测过程中如果发现 TPS 上不去,或者响应时间一直往上涨,定位路径一般遵循下面的顺序:

第一,确认压测端自身没有达到资源瓶颈。打开压测机器的监控,看看 CPU、内存、网络是否已经打满。如果压测端撑不住了,可以先增加 worker 机器。

第二,观察服务端的基础监控。看 CPU 是全线飘红还是局部高。CPU 高就要进一步看是用户态高还是内核态高,用户态高说明业务代码在密集计算,内核态高可能跟系统调用和网络中断有关系。

第三,排查数据库、缓存、消息队列这些依赖组件。这个环节往往有决定性证据。数据库慢查询数量是否暴增,连接数是否占满,缓存命中率是否下降,所有这些问题都会直接反应在 TPS 曲线上。

第四,看网络层。有没有丢包,TCP 重传率高不高,是否出现了带宽打满。有时候业务代码没任何问题,纯粹是带宽不够导致整体吞吐量上不去。

这里我分享一个踩过的坑:有次压一个文件上传接口,压到 100 并发时 TPS 就上不去了,响应时间还在缓慢攀升。从压测端到服务端都排查了一圈,最后发现是上传流量把出口带宽打满了。把带宽从 100Mbps 扩到 1Gbps 后,同样并发下 TPS 直接翻了两倍多。性能压测问题很多时候不是某一个组件的问题,而是一条链路上最薄弱环节的问题。

6. 常见问题与独家避坑经验:我能想到的坑都给你列出来

6.1 安装了却找不到命令

如果你用pip install locust安装后,在命令行里输入locust提示找不到命令,大概率是 Python 的 Scripts 目录没有加入 PATH 环境变量。这种情况最直接的解药是用python -m locust来替代:

python -m locust -f locustfile.py

另外长期建议是把 Python 的 Scripts 目录配置到 PATH 中,避免后续还会遇到其他命令行工具同样的问题。

6.2 任务权重不生效?检查你的方法名

@task 装饰器只对类中的方法生效,如果你误把某个方法写成了不带装饰器的普通方法,那么该方法的调用概率实际上是 0,压测永远不会执行这个操作。这是个非常隐蔽的错误,因为它不会抛任何异常。脚本跑得热火朝天,结果所有流量都集中在另一个方法上。

我建议你在压测启动后,先到 Web 界面看一眼各个 Task 的请求统计。哪个 Task 没有产生请求,就先回头检查是不是漏了装饰器或者self.client写成了裸requests。

6.3 用户并发数很大但 RPS 很低:不要被数字欺骗

启动 1000 个虚拟用户,却发现总体每秒请求数只有几十,这是新手最容易困惑的场景。原因通常在于等待时间配得太长。如果每个用户wait_time = between(5, 10),那么 1000 个用户并发,理论上每秒钟的请求量上限也就是 1000/7 ≈ 143 左右,实际还会更低。

所以要判断服务的真实承受能力,要么降低 wait_time,要么用constant(0)做极限测试。用户数和 TPS 之间不是等号关系,隔着一个“思考时间”的除法关系,这个换算逻辑在压测前就要想清楚。

6.4 请求乱序和关联依赖:正确处理接口间的上下文

有些接口之间有严格的前后依赖。比如你先要拿到订单 ID,才能去调用支付。这种关联如果处理不好,会直接导致整个压测会话的失败率飙升。

解决办法是把前置操作写在on_start里,获取到上下文后存到实例变量中:

class OrderUser(HttpUser): def on_start(self): resp = self.client.post("/api/order", json={"items": [1, 2, 3]}) self.order_id = resp.json()["order_id"] @task def pay_order(self): self.client.post(f"/api/order/{self.order_id}/pay")

这样每个用户在自己的生命周期内,都持有独立的order_id,不会发生交叉串数据的情况。

注意:在压测场景里,使用全局变量存储用户上下文数据是大忌。多个协程同时读写同一个变量,会产生数据混乱和不可预知的失败。所有上下文数据都应该通过实例变量维护,确保每个用户的数据是独立的。

6.5 文件上传和流式请求:突破基础用的限制

普通的 JSON 接口用self.client.post(json=...)就能搞定。但如果你需要压测文件上传接口,或者模拟文件流式上传的场景,可以这样写:

class UploadUser(HttpUser): @task def upload_file(self): with open("sample.csv", "rb") as f: self.client.post("/api/upload", files={"file": ("sample.csv", f, "text/csv")})

这里复用了requests的files参数语法,同时仍然被 Locust 统计请求耗时。如果你想要压测一个下载大文件的接口,可以开启流式响应,避免每个请求都等整个文件传输完成:

class DownloadUser(HttpUser): @task def download_large_file(self): with self.client.get("/api/export", stream=True) as resp: if resp.status_code == 200: for _ in resp.iter_content(chunk_size=1024): pass

流式读取能够模拟真实客户端接收完整响应的场景,同时不会因为大量响应数据滞留在内存中导致压测端进程崩溃。

6.6 脚本调试时观察单个请求的响应细节

压测过程中脚本里的 print 输出会非常混乱,因为成千上万的协程在同时输出。如果你只想调试一个接口的响应内容,建议写一个单独的小脚本,然后用response.text或response.json()打印出来查看:

from locust import HttpUser class DebugUser(HttpUser): @task def debug(self): resp = self.client.get("/api/test") print(resp.status_code, resp.text)

然后在命令行先以单用户、单次任务的方式运行,比如:

locust -f locustfile.py --headless -u 1 -r 1 --run-time 5s

这样就能在输出中清楚地看到单个请求的完整响应内容,定位问题后再移除调试代码,回到正常的压测模式。

7. 与 JMeter 的实测对比:帮你做出最终的选择

不少团队在引入 Locust 时会面临一个问题:要不要把原有的 JMeter 脚本迁过来。我的建议是,不要轻易全量替换,而是看场景选工具。

对比项LocustJMeter
脚本表达方式Python 代码XML 文件 + GUI 操作
学习成本需要 Python 基础,但逻辑表达直观需要理解组件体系,入门陡峭
并发模型协程,单机并发能力强线程,单机并发上限受制于线程数
扩展性极为灵活,能够与测试框架和 CI 深度集成适合常见协议,复杂业务逻辑编写吃力
分布式主从模式,配置简单主从模式,配置略繁琐
调试体验代码调试效率高,容易复现问题日志输出复杂,排查问题成本高
适用场景复杂业务逻辑、快速迭代的互联网服务多协议兼容、已有成熟用例的复杂系统

我个人的经验法则非常简单:如果你的被测服务以 HTTP/HTTPS 为主,且团队里有人会 Python,直接选 Locust。如果涉及大量非 HTTP 协议,比如 JDBC、JMS、FTP 等,JMeter 的开箱即用能力还是明显更占优,强行用 Locust 做这类协议反而要写一堆底层代码,得不偿失。

8. 最后的实战建议:把 Locust 真正用出价值

文章到了这个位置,你已经掌握了 Locust 的核心用法。但懂得 API 和用得上手是两个层次,我结合自己的经历,给你几条关于实战的真心建议:

第一,永远不要只压一个接口就写报告。性能测试是一个从基准、到场景、再到稳定性的阶梯过程。先单接口摸上限,再混合场景模拟真实流量,最后做长时间稳定性测试观察内存和连接数会不会泄漏,每一步缺一不可。

第二,性能测试必须和监控配套。压测机给出的指标只是前半截信息,被压服务器的 CPU、内存、磁盘、网络、数据库连接、JVM 堆栈这些指标才是定位瓶颈的后半截证据。没有监控的压测就像闭着眼睛开车,数据再好看也没有意义。

第三,脚本要像业务代码一样对待。该封装封装、该参数化参数化、该写注释写注释。压测脚本不是一次性用品,同一个服务的回归压测会反复用到,坏味道的脚本会严重拖累后续效率。

第四,多用 CSV 保存数据。每次压测的结果都留档,长期累积下来就是一套非常宝贵的容量基线数据。以后做容量规划、性能调优、甚至排查线上事故,都有底可查。

我最后再分享一个让我印象很深的项目经历。某次做核心接口的容量评估,用 Locust 压测时发现只要并发超过 200,TPS 就停止增长,响应时间持续走高。一开始怀疑服务端资源不足,结果监控显示各项指标都远没到上限。后来定位到是压测机网络出口带宽被其他压测任务挤占了,几百个并发用户的请求一多,带宽直接被占满,导致大量请求排队等待发送。调整了压测机网络环境和网络规划之后,同样的脚本、同样的并发,TPS 直接翻倍到预期水平。那次之后我养成了一个习惯:任何一次压测启动前,都要先确认压测机、被测机、网络链路这三者的基础资源都没有“外来干扰”,排除了环境问题再动业务层面的排查。

性能测试这个活儿,表面上比的是工具有多先进,实际拼的是思路清晰和细节掌控。Locust 给你提供了足够锋利的一把刀,剩下怎么用好,就看你自己的实战经验了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询