资讯动态

Locust压测实战:从入门到分布式高并发性能测试

发布时间:2026/10/1 16:14:29 来源:尧图企业网站定制
1. 先搞清楚 Locust 是什么再决定要不要用它做后端开发和测试的同学早晚都会遇到一个问题接口写得对不对、性能扛不扛得住不能靠肉眼看代码得拿真实并发去压一下。这时候就轮到性能测试工具出场了。业界最常见的方案有 JMeter、wrk、ab、Gatling还有就是今天要聊的 Locust。Locust 是一个基于 Python 的开源负载测试工具核心思想特别简单用 Python 代码描述用户行为然后让成千上万个虚拟用户同时去执行这些行为。它和传统压测工具最大的区别在于——你的压测脚本本身就是标准的 Python 代码不是 XML、不是 DSL、不是图形拖拽就是纯代码。这意味着你想怎么折腾就怎么折腾可以把登录、下单、支付整条链路串起来可以做参数化可以按业务逻辑动态走分支。它内部基于协程模型实现高并发单机就能模拟几千上万的用户不像线程模型那样吃内存。自带一个 Web 界面启动后浏览器打开就能看到实时请求成功率、响应时间、吞吐量这些指标也能动态调整并发数而不需要重启压测。工作几年下来我用它压过电商大促前的核心链路、压过消息推送服务的网关、也压过内部中台的上传接口稳定性和灵活性基本没掉过链子。这篇文章面向的是第一次接触 Locust 的人我会从环境准备讲起到手写第一个压测脚本、跑起来看结果再到分布式压测和常见坑把一套能直接落地的玩法完整过一遍。就算你之前没写过 Python 代码照着操作也能跑通。2. Locust 的核心设计理解这几条就不容易用歪2.1 协程并发为什么单机性能能打要理解 Locust 为什么快得先搞明白它和 JMeter 的并发模型差异。JMeter 每个虚拟用户是一条线程线程虽然比进程轻但终究要占不小的内存和上下文切换开销。一台普通的 4C8G 机器用 JMeter 跑到两三千并发往往就有点喘了。Locust 不一样它基于 gevent 协程库底层只用少量真实线程配合事件轮询把大量虚拟用户调度在协程上。你可以把协程理解为轻量级的“微任务”切换成本极低所以单台机器模拟几万用户是常有的事。我自己在实际压测里4C8G 的机器跑 5000 并发很轻松CPU 也没打满。要注意的是这只针对 I/O 密集型的 HTTP 请求如果脚本里有大量 CPU 计算协程模型优势就会被削弱。2.2 脚本即代码灵活性来自 Python 本身Locust 的设计哲学是“一切皆代码”。用户行为、请求参数、断言逻辑、数据准备全都自己写在 Python 文件里。这带来一个实打实的好处你可以用 requests、numpy、pandas 这些库来做非常复杂的场景构造。比如需要从 Redis 里取一批 token 去压测或者要按正态分布随机生成请求体在 JMeter 里搞这套非常费劲在 Locust 里就是几十行 Python 的事。当然凡事有两面这也意味着团队里至少得有一个人会写 Python。如果一个完全不会编程的人要上手压测JMeter 的图形界面上手门槛反而更低。所以工具选型从来都是取舍没有绝对好坏。我个人观点是只要团队能写代码Locust 的长期维护成本和灵活性优势非常明显。2.3 HttpUser 和 User绝大多数场景用前者Locust 里有两个核心类一个是User一个是HttpUser。User是所有虚拟用户的基类只负责任务调度和等待时间控制HttpUser在它的基础上集成了 HTTP 客户端每个虚拟用户拥有自己的 session能自动管理 Cookie、请求头等状态。新手刚上手不用纠结直接继承HttpUser就对了。说一个我踩过的坑Locust 2.x 版本里每个 HttpUser 实例会自动创建一个 requests.Session这就保证了压测过程中登录态的 Cookie 是隔离的模拟的是不同独立用户。如果你不小心用了全局 session所有虚拟用户共享一个登录态那压出来的结果没有任何参考价值。3. 环境准备与安装这一节照着抄就行3.1 Python 环境选择Locust 是纯 Python 项目所以第一步得先有 Python。官方目前要求 Python 3.8 及以上我建议直接用 3.10 或 3.11别用太老的版本。操作系统层面 Windows、macOS、Linux 都支持但如果你想要极限并发性能和后续分布式压测Linux 是最合适的环境。这里我不建议你往系统自带的 Python 里直接装包因为很容易把系统环境搞乱装错一个包导致其他项目跑不起来。我的习惯是给压测专门建一个虚拟环境隔离干净删了重建都不心疼。如果你不知道怎么建虚拟环境复制下面这组命令Windows 系统在命令提示符或 PowerShell 里跑macOS/Linux 在终端里跑# 先确认 python 版本 python3 --version # 创建虚拟环境目录名用 venv python3 -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate激活之后命令行前面会多出一个(venv)前缀这就说明你现在在虚拟环境里了。后续安装的所有 Python 包都只会装在这个目录里不会污染系统环境。3.2 用 pip 安装 Locust确认虚拟环境已激活直接执行安装命令pip install locust如果网络条件不太好的话可以换成国内镜像源速度会快很多pip install locust -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成之后验证一下版本locust --version正常情况下会输出类似locust 2.31.6这样的版本号。如果你看到command not found或者“不是内部或外部命令”大概率是虚拟环境没激活成功或者 pip 安装路径没加入 PATH。检查一下前面虚拟环境激活步骤是否执行正确。3.3 安装完成后必做的两个小验证版本号出来只是第一步我建议你再做两个简单验证确定安装真的干净。第一验证 gevent 是否正常。Locust 的协程依赖 gevent偶尔会有人在编译 gevent 时出现问题。执行python -c import gevent; print(gevent.__version__)能输出版本号就说明没问题。第二验证 Locust 命令能否正常打印帮助信息locust --help能列出参数列表就说明主程序入口没问题。这两步做完环境就算彻底稳了。注意如果你的机器上同时装了多个 Python 版本一定要用python3 -m pip install locust这样的明确方式避免装到了错误的 Python 环境里。我见过最离谱的情况是一个人在 Windows 上有 Python 3.7 和 3.10 两个版本pip 装到了 3.10命令行却默认调用了 3.7排查了半天才发现是环境错位。4. 第一个压测脚本5 分钟内把高频接口压起来4.1 最小可运行脚本长什么样环境装好后新建一个文件命名随意我习惯叫locustfile.py。这是 Locust 的默认文件名如果你用这个名字运行命令时可以省略-f参数指定的文件路径省事很多。把下面这段代码复制进去from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time between(1, 3) task def get_home(self): self.client.get(/)这段代码干了三件事继承HttpUser定义了一个虚拟用户类给每个用户配上 1 到 3 秒的随机等待时间然后用task注册了一个请求任务目标地址后面会通过命令行参数传入。理解成“每个虚拟用户每隔 1-3 秒访问一次首页”就对了。4.2 启动压测并观察 Web 界面在命令行执行locust -f locustfile.py --host http://example.com然后浏览器打开http://localhost:8089就是 Locust 自带的控制台界面。页面上有两个关键输入框一个是 Number of users模拟用户总数一个是 Spawn rate每秒启动的用户数。我的习惯是先填 20 个用户、每秒 5 个观察一两分钟确认接口没有问题再把并发提上去而不是一上来就填 10000。做过压测的人都知道起步阶段爬坡观察比一把梭要安全得多尤其是压测试环境还好如果误压到生产环境慢点起步能给你留出紧急停止的反应时间。页面点击 “Start swarming” 之后刷新一下就能看到 RPS、平均响应时间、中位数、百分位、失败率这些指标。其中我重点关注两个数RPSRequests per second系统每秒能处理多少请求反映吞吐能力。95% 响应时间95% 的请求在多少毫秒内完成比平均值更能反映真实体验。4.3 命令行启动的替代方案Web 界面很好用但如果你需要在服务器上做定时压测或者自动化流水线用 Web 界面就不合适了。Locust 支持全命令行模式locust -f locustfile.py --host http://example.com --headless -u 100 -r 10 -t 5m参数说明--headless不启动 Web 界面直接开始压测。-u 100总共模拟 100 个用户。-r 10每秒新增 10 个用户。-t 5m压测持续 5 分钟后自动结束。命令行模式结束时会打印一份汇总统计表包含请求总数、失败数、每秒请求数、各分位响应时间。这些数据可以直接粘给同事或者写进测试报告里。注意-t的取值格式可以是5m、30s、1h这种m 代表分钟s 代表秒h 代表小时。别把 5m 理解成 5 分钟然后填了个 5h一觉醒来压测还在跑这是我身边人真实犯过的错。5. 脚本进阶把压测场景写得像真实用户5.1 使用 wait_time 控制请求频率刚才的最小示例用了between(1, 3)表示每个用户每次任务执行完后随机等待 1-3 秒。这个参数直接影响压测的请求速率用途完全不同。constant(2)固定等待 2 秒适合模拟匀速的定时任务型调用。between(1, 5)随机等待 1-5 秒适合模拟真实用户的浏览节奏。constant_pacing(2)每秒最多发起 1 个任务适合控制请求频率上限。举一个实际场景压一个查询接口真实用户在页面上操作不会像机器一样每秒狂点正常会停顿几秒钟看看内容再点下一个。用between(1, 5)模拟这种节奏得到的响应时间指标会比无脑高并发更真实。5.2 任务定义的三种写法task是最直观的写法但任务多了、逻辑复杂了可以换几种更清晰的组织方式。方式一直接用装饰器并配权重from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time between(1, 3) task(3) def view_home(self): self.client.get(/) task(1) def view_cart(self): self.client.get(/cart)task(3)和task(1)表示权重也就是 3/4 的虚拟用户会访问首页1/4 的虚拟用户会访问购物车。这个权重非常实用可以让压测流量更符合真实业务的流量比例。方式二使用 TaskSet 将任务分组from locust import HttpUser, task, between, TaskSet class UserBehavior(TaskSet): def on_start(self): self.client.post(/login, json{username: test, password: 123456}) task(2) def index(self): self.client.get(/) task(1) def profile(self): self.client.get(/profile) class WebsiteUser(HttpUser): wait_time between(1, 3) tasks [UserBehavior]on_start是每个虚拟用户启动时执行的方法非常适合做登录、获取 token 这类前置准备。TaskSet 的作用是把一组相关任务打包方便复用和维护。方式三动态任务集合from locust import HttpUser, task, between class MyUser(HttpUser): wait_time between(1, 3) task def my_task(self): if self.environment.runner is not None: pass这种场景相对少见主要用于需要在任务里动态判断压测状态的情况。新手了解前两种就够了。5.3 参数化数据让一万个用户不是同一张脸真实系统的用户不是同一个所以压测数据最好也是多样化的。如果一万个虚拟用户全部用同一个账号、同一个订单号去请求很可能还没压到系统瓶颈先把数据库的唯一索引给触发了一遍返回一堆 Duplicate 错误压测结果彻底失真。最简单的参数化做法是用 Python 内置的队列让每个用户取到不同的数据import random from queue import Queue from locust import HttpUser, task, between user_ids [fuser_{i} for i in range(10000)] data_queue Queue() for uid in user_ids: data_queue.put(uid) class WebsiteUser(HttpUser): wait_time between(1, 3) task def get_order(self): uid data_queue.get() self.client.get(f/order/{uid}) data_queue.put(uid)这段代码的思路是启动时把所有 user_id 放入队列每个虚拟用户执行任务时从队列取一个用完再放回去。这样保证几乎所有请求都用不同的用户 ID系统层面不会再因为同一个 ID 打架。5.4 用 check 断言验证返回结果压测不只是看接口能不能通还要确认返回内容是否符合预期。比如下单接口返回了 200但业务状态码是失败这种请求要不要被统计成失败肯定要。Locust 提供了response对象的断言方法from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time between(1, 3) task def create_order(self): with self.client.post(/orders, json{item_id: 123, count: 1}) as resp: if resp.status_code 200: data resp.json() if data.get(code) ! 0: resp.failure(f业务失败: {data.get(msg)})resp.failure(message)会把这条请求标记为失败并记录失败原因。这样压测报告里的失败率就是真正的业务失败率而不是只看 HTTP 状态码。注意如果响应体很大频繁解析 JSON 会额外消耗 CPU影响压测机自身的性能。建议只提取关键字段做断言不要对完整响应体做深度的结构化解析。我个人遇到过的一个坑是压测一个文件上传接口每次响应包含几 MB 的临时下载地址脚本里做了完整 JSON 解析结果压测机 CPU 比被测服务器还高后面改成只匹配状态码才正常。6. 分布式压测单台机器真撑不住的时候怎么扩容6.1 什么时候需要分布式单台压测机能模拟的用户数量终究有限虽然有协程加持但网络带宽、文件句柄数、CPU 还是会成为瓶颈。如果你要把并发压到两三万以上或者压测目标本身响应非常快、单机 RPS 需要拉到几万这时候就得考虑用多台机器组成分布式压测集群。另一种常见场景是压测的机器和被压的服务部署在同一台机器上那压测机本身就会偷走 CPU 和内存压出来的数据完全不可信。专业的做法是压测机独立部署至少不能和被压服务共享同一台物理机。6.2 一主多从的配置方法Locust 的分布式模型非常直白一台机器作为 master负责协调和汇总结果其他机器作为 worker只负责执行压测流量。假设你有两台机器机器 Amaster执行locust -f locustfile.py --master --host http://example.com机器 Bworker执行locust -f locustfile.py --worker --master-host机器A的IP几台 worker 就重复执行几次。启动完成后你只需要打开 master 的 Web 界面http://机器A的IP:8089所有 worker 的数据会汇总到这里统一展示。整个控制体验和单机模式完全一样。需要注意的点master 只负责调度和汇总不产生压测流量所以不需要高配置。worker 必须能访问到压测目标服务master-host 通常填内网 IP不要走公网。所有机器的 locustfile.py 必须保持一致否则 worker 执行的任务可能不一致。如果用了自定义的数据文件比如 CSV 里的账号密码每台 worker 机器上都要有这份文件。6.3 分布式环境下的数据隔离问题之前提到参数化时用了 Queue这在分布式环境下有一个大坑Queue 数据只存在单台机器内存里master 上生成的用户数据并不会同步到 worker。这会导致每台 worker 用到的是自己本地的数据如果数据量不够大两三个 worker 就把一份账号池取完了后面全是重复数据。解决办法是在每台 worker 启动前独立生成一份数据文件或者连接同一个 Redis 从里面取数。我在实际项目里更推荐后者把测试账号统一放到 Redis 里所有 worker 从 Redis 拉取既解决了数据重复问题也方便动态调整账号数量。当然如果只是少量测试数据直接在每个 worker 本地初始化一份也可以。7. 常见问题与排查技巧实录7.1 命令找不到 / ModuleNotFoundError症状是敲locust提示找不到命令或者 Python 里import locust直接报错。最常见的原因就是虚拟环境没有激活或者 pip 安装到了别的 Python 版本里。排查步骤which python which pip pip --version python -m pip show locust确认这三个命令指向的路径在同一个环境里。如果 pip 显示的是/usr/bin/pip而 python 是虚拟环境里的那明显装错地方了。用python -m pip install locust可以把包装到当前 python 对应的环境里。7.2 明明并发数很大RPS 却上不去这个问题我见过太多人问。并发用户多不代表请求就多每个用户有 wait_time如果用户大部分时间在等待那 RPS 自然不高。这本身不是 bug而是你的场景设计如此。如果你就是要追求高 RPS把 wait_time 设置很短比如constant(0.1)或者直接不用等待配合大并发数RPS 就会明显上去。同时也要检查压测机本身有没有到瓶颈打开任务管理器或者top命令看一眼 CPU 和内存。还有一层容易被忽略的Linux 系统的ulimit -n文件句柄数限制。高并发下大量的网络连接会消耗文件描述符默认值往往只有 1024根本不够用。改之前先看当前值ulimit -n如果是 1024可以通过修改/etc/security/limits.conf来提高具体修改方式网上有很多这里不展开了。修改之后建议重启下终端或者重新登录让配置生效。7.3 Web 页面打不开启动 Locust 后浏览器访问localhost:8089打不开先检查命令行有没有报错占用了端口。换一个端口启动locust -f locustfile.py --host http://example.com --web-port 8090如果你的操作环境是远程服务器需要访问公网 IP 加端口记得在云服务器的安全组或者防火墙里放行对应端口。7.4 压测过程中远程服务器连接卡顿有一种情况是压测机的并发数开得太大直接把自己压挂了。我在一次压测中把用户数从 5000 提到 10000压测机的 CPU 瞬间打满SSH 连接都敲不动命令了。后来强制重启后我学乖了压测机最好单独弄一台或者在压测启动前预留一个 shell 窗口用top时刻盯着性能指标。如果确实需要大并发分布式是更稳妥的方案。把并发拆到多台 worker 上每台承担一部分既能突破单机瓶颈也不会出现压测机自己先挂掉的尴尬。7.5 常见问题速查表问题可能原因解决思路locust 命令找不到虚拟环境未激活或安装错误检查 python/pip 路径重新激活环境Web 界面访问不了端口被占用或防火墙拦截换端口检查安全组和防火墙RPS 与并发数不匹配wait_time 过长或系统瓶颈缩短 wait_time检查压测机 CPU/内存失败率突然飙升接口逻辑问题或参数化数据重复查看失败原因检查断言逻辑和数据池单机并发上不去文件句柄限制或资源耗尽调高 ulimit拆分 worker分布式数据不一致各 worker 本地数据不同步用 Redis 等外部存储统一管理测试数据8. 一点经验之谈用 Locust 这几年我的体会是压测工具本身并不复杂真正的难点在于怎么设计出贴合真实生产场景的压测脚本以及怎么解读压测数据。工具只是放大镜能不能看出系统的瓶颈取决于你对业务的理解和对数据的判断。如果你打算把它用到正式项目里我建议你从最简单的脚本开始把一个高频接口压起来跑通整条链路。然后逐步往脚本里加登录态、参数化、断言最后再考虑分布式。每一步实测下来你会对系统在压力下的表现形成自己的直觉——比如响应时间到多少毫秒用户会开始抱怨RPS 涨到多少后数据库连接数会告警这类实战直觉是看任何文档都学不来的。另外还有一个容易被忽视的小技巧压测数据记录尽量保留。我自己习惯每次压测后把 Locust 生成的统计数据和当时的系统监控截图一起存档下次优化后再压一组对比。有了纵向对比数据优化到底有没有效果、提升了多少一目了然比“感觉快了一点”有说服力得多。这套流程跑通之后你会发现 Locust 能做的事情远不止接口压测。后面有需要的话可以尝试接入 Prometheus 做指标采集或者写个 Python 脚本定时拉取压测数据自动生成日报。工具的上限基本取决于你愿意投入多少精力去折腾。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑