资讯动态

用Locust做网站压测:从用户行为建模到分布式实战

发布时间:2026/10/20 7:39:02 来源:尧图企业网站定制
用Locust做网站压测最舒服的地方不是它有多快而是写场景的方式几乎不用换脑子。JMeter、ab、wrk这些工具我早年都用过很多场景确实能做但一旦你想把“用户登录后浏览商品、加购物车、再下单”这种完整业务流程变成压测脚本用代码描述会比在GUI里拖拽节点、填字段爽得多。这篇东西我就按自己从入门到落地的实际路径来写先讲清楚Locust的核心机制和选型逻辑再给出最小可运行脚本然后逐步把场景写复杂、把压测从单机扩展到多机最后聊聊怎么看指标、怎么排查问题。不管你是刚接触负载测试还是之前用过其他工具想切到Locust照这份思路走半天内应该能跑出自己的第一份有效报告。1. 为什么选Locust先看清它的设计取向很多人第一次听说Locust是被“用Python写压测脚本”这个点吸引的。但它的设计逻辑远不止“换成代码写脚本”这么简单。Locust的核心模型是把每个虚拟用户看作一只独立的“蝗虫”你用代码定义这只虫的行为然后让成千上万只虫同时涌向目标系统。这个思路和进程/线程模型最大的区别是单个进程内可以挂几万甚至几十万并发用户而不需要同步开那么多系统线程。能做到这一点靠的是协程和事件驱动。选型这件事我一直建议先把工具背后的模型搞清楚不然很容易出现“工具会用但压测结果解释不了”的情况。Locust里几个最关键的概念HttpUser代表一类发起HTTP请求的虚拟用户task是用户会去执行的动作wait_time模拟用户在两次操作之间的思考时间。你写的locustfile本质上就是一份“用户行为说明书”而不是一份接口清单。1.1 压测工具选型Locust和JMeter的核心差异既然提到选型就展开说下。JMeter的生态成熟、插件多但脚本本质是XML你调整一个业务逻辑往往要在GUI里层层点开想把脚本纳入Git做版本管理也很别扭。wrk这类工具适合压单接口但它不走真实协议栈也很难描述“一批用户先登录再访问”这样的链路。ab更不用说最多拿来做个快速冒烟。Locust最大的优势在于测试脚本就是普通Python代码可以复用工程里的工具函数、数据构造逻辑天然支持Git分支、Code Review、CI集成。它不像JMeter那样“测试是独立的一套体系”而是可以和你现有的研发流程无缝衔接。缺点也很明确压测能力上限和你的写码水平挂钩脚本写得不好压测结果就失真。而且Locust的报告图表功能比JMeter轻量不少需要持续观测更细粒度的监控数据时通常还要自己接一套监控平台。1.2 用户、任务、等待时间拆穿Locust的三个核心名词把Locust的模型翻译成自然语言其实就是一个虚拟用户在一个循环里反复干三件事从任务列表里按规则选一个任务执行一系列HTTP请求然后等待一段时间再进入下一轮循环。用户User不是“一个人”而是“一种行为集合”。一个HttpUser类里可以定义多个任务所有虚拟用户都从这个类的任务池里选任务执行。任务Task就是你希望用户执行的动作通常对应一组HTTP请求。任务可以带权重权重高意味着被选中的概率更大。等待时间wait_time用户执行完一个任务后暂停的时长用来模拟真实用户读屏、思考、填表的时间。常用between(1, 5)表示在1到5秒之间随机等待。这三者组合就可以模拟出一个基本合理的用户行为模型。很多人刚开始压测时喜欢把所有请求都写成无脑循环RPS确实能打上去但压测结果更像DDoS而不是用户访问参考价值很低。理解了“用户行为”和“纯流量”的区别之后才会发现Locust真正值钱的地方在于它能把“像不像真实用户”这件事量化出来。2. 环境准备与第一个最小脚本环境这块基本没什么坑。Locust是Python的第三方库直接通过pip安装即可。建议用Python 3.9以上版本新版Locust对异步和协程的支持更稳定后续如果要跑分布式压测依赖版本也更好管理。2.1 安装与项目结构建议创建一个独立的虚拟环境比如用venv或者conda都行然后在项目目录下执行pip install locust安装完成后可以用locust --version确认版本。项目结构上我习惯这样组织loadtest/ ├── locustfile.py # 压测主脚本 ├── testdata/ │ ├── users.csv # 测试账号数据 │ └── products.csv # 商品ID等参数化数据 ├── reports/ │ └── result.csv # CSV结果导出 └── requirements.txt之所以单独放testdata目录是因为真实业务压测几乎都会用到参数化数据。把所有数据塞在locustfile里代码会变得很臃肿而且不好复用。从一开始就养成“脚本和数据分离”的习惯后面维护成本低很多。2.2 最小可运行脚本一个locustfile就够了先写一个最小脚本体验一下全流程。新建locustfile.pyfrom locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time between(1, 3) task def visit_home(self): self.client.get(/) task def visit_about(self): self.client.get(/about)这个脚本定义了两件事每个虚拟用户在两次请求之间等待1到3秒有两个任务首页和关于页访问权重一样。在终端里启动locust -f locustfile.py --hosthttps://example.com启动后控制台会提示访问本地的http://127.0.0.1:8089。浏览器打开这个地址就是Locust的Web控制台在这里设置要模拟的用户总数和每秒生成速率点击开始就能看到实时压测数据。第一次跑的时候我建议用非常小的规模验证流程10个用户每秒生成1个跑两分钟。这个规模足以让你确认脚本本身没有逻辑问题也不会给目标系统造成意外压力。2.3 Web控制台操作与首次结果观察Locust的Web控制台核心参数就两个Number of users最终要达到的并发用户数。Spawn rate用户生成速率也就是每秒有多少个新虚拟用户加入。理解这两个参数的配合很关键。如果直接设置1000个用户、每秒1000个生成所有用户会在1秒内同时涌入对系统来说等同于瞬时冲击如果设置1000个用户、每秒10个生成系统会经历一个逐步加压的过程你能更直观地看到系统在哪个并发量级开始出现响应时间拐点。项目里正式压测时我一般会先给一个较低的spawn rate做预热让服务完成连接池、缓存等预热逻辑再开始记录有效数据。Web界面启动后你会看到实时更新的指标请求数、RPS每秒请求数、平均响应时间、各百分位响应时间、失败率等。右上角还有图表能看出整个压测过程中响应时间的变化趋势。这里有个新手容易忽略的点图表默认展示的是全量历史数据平时看趋势还可以但如果你要定位某个时间窗口的问题最好先关注某个固定时间段的切片或者干脆把数据导出后用脚本分析不然很容易被平均值误导。3. 编写更贴近真实业务的压测场景最小脚本只是用来验证流程的。真正有价值的压测场景一定得贴近业务。这里说的“贴近”不光是URL接口列表齐全还包括用户行为的比例分布、数据准备、思考时间是否符合真实使用习惯。3.1 任务权重与嵌套任务集让流量分布有业务意义真实系统的访问量一定不是均匀分布的。比如一个电商网站首页访问量最大搜索次之提交订单的比例通常很小。如果你把所有任务权重写成一样压测出来的瓶颈和真实业务瓶颈大概率对不上。Locust的task装饰器可以直接传权重class WebsiteUser(HttpUser): wait_time between(0.5, 3) task(10) def view_home(self): self.client.get(/) task(4) def search_product(self): self.client.get(/search?qkeyboard) task(1) def submit_order(self): self.client.post(/orders, json{product: keyboard, qty: 1})权重10/4/1的意思是在大量循环中首页任务被选中的概率是10份搜索是4份下单是1份。这种比例能让压测流量更接近真实分布。如果你需要一个任务内部包含固定顺序的多个请求可以在TaskSet里定义嵌套任务。比如“浏览商品详情”这个任务可能需要先打开商品页再查看评论接口最后添加购物车三个请求有顺序关系。把这三个请求放进同一个TaskSet再把这个任务集挂到用户类上就能保证它们按照业务顺序执行而不是被随机打散。3.2 登录态、Token与参数化数据准备几乎所有的业务压测都绕不开登录态。Locust的HttpUser自带client对象这个client在同一个虚拟用户的生命周期内会保存Cookie所以你在on_start里做一次登录后续任务请求就能自动携带会话信息。class WebsiteUser(HttpUser): wait_time between(1, 5) def on_start(self): resp self.client.post(/api/login, json{ username: self.username, password: self.password, }) token resp.json()[token] self.client.headers.update({Authorization: fBearer {token}}) task def get_profile(self): self.client.get(/api/profile)但这里有个实战里非常容易踩的坑如果你让所有虚拟用户都使用同一个账号登录压测结果会严重失真。一是服务端可能对单账号有并发限制或风控逻辑二是分布式场景下多台机器的同一账号登录可能直接把账号踢下线。更合理的做法是准备一批测试账号给每个虚拟用户分配一个独立账号数据量至少不小于并发用户数。账号可以放在CSV文件里启动时读取后按索引分配。另一个容易被忽略的问题是Token的过期时间。如果压测场景要跑10分钟而Token有效期只有5分钟那后半段所有请求都会因鉴权失败而不停报401。这种失败不是你压测的目标系统的真实性能问题而是脚本设计问题。遇到这种场景最稳妥的办法是在任务中定期检查响应状态如果出现401就重新登录并刷新Header。3.3 思考时间和节流别让压测变成DDoSwait_time看似简单实际是决定压测真实性的关键点。很多新手为了把RPS冲高把wait_time设成0甚至constant(0)结果就是所有虚拟用户都在无脑快速循环目标系统的表现会极其糟糕但这种糟糕不代表真实用户场景下的问题。给wait_time建模时先想清楚业务系统的真实用户节奏。如果是后台管理系统用户操作频率低思考时间可以设到3到10秒如果是高并发的资讯站用户翻页速度较快可以设到0.5到2秒。不同任务之间也可以使用不同的等待时间Locust允许你在任务内部用self.wait()手动控制等待灵活度很高class WebsiteUser(HttpUser): def my_task(self): self.client.get(/) time.sleep(0.5) self.client.get(/next)用time.sleep和between的区别在于between是Locust框架层面的调度等待time.sleep会直接阻塞当前协程。两者都能用但我更推荐使用wait_time机制因为它在分布式模式下更利于框架统一管理调度节奏。4. 分布式压测从一台机器到多台机器单机跑压测受限于CPU、内存和网络带宽虚拟用户数量到了一定级别就上不去了而且结果也不稳定。Locust设计之初就支持分布式扩展方式非常简单。4.1 本机多进程模式充分利用多核CPU先说说最简单的一种情况你有一台配置不错的机器想跑比较大的并发。由于Python GIL的限制单进程很难充分利用多核CPULocust提供了--processes参数可以在本机直接拉起多个worker进程locust -f locustfile.py --hosthttps://example.com --processes 4这个命令会启动一个master进程和4个worker进程。master负责汇总数据、提供Web UIworker负责实际产生压力。多进程模式下每个worker都是独立的Python解释器互不干扰能够更充分地利用多核性能。这个方案适合大多数中小规模压测尤其是当你没有多台压测机可用的时候。需要注意的一点是processes数量并不是越多越好。worker进程之间会通过网络通信向master上报数据进程数量太多时master本身可能成为瓶颈。一般建议先看机器CPU核数从4到8个进程开始尝试观察CPU利用率和数据汇总延迟再做调整。4.2 多机主从模式真正的横向扩展当单机已经不满足需求就需要把压测跑在多台机器上。Locust的主从模式思路很简单一台机器作为master负责调度和数据汇总其他机器作为worker负责执行任务。在master机器上执行locust -f locustfile.py --master --master-bind-host0.0.0.0 --master-bind-port5557在每台worker机器上执行locust -f locustfile.py --worker --master-hostmaster的IP --master-port5557执行前需要确保所有worker机器上都安装了相同的Locust版本locustfile内容也必须一致。如果脚本里引用了测试数据文件也要一并同步到各台worker机器上。多机模式下有几个细节我要特别提醒。第一master和worker之间默认走5557端口测试前要确保防火墙放行。第二worker机器的时间尽量和master保持同步虽然Locust的时间统计不是完全依赖机器时钟但时间偏差过大确实会影响任务调度的节奏。第三不要在压测中途随意改脚本然后只同步到部分worker保持版本一致是分布式压测的底线否则你会得到一份各段数据含义完全不同的混合报告。4.3 无界面模式把压测接入CI流程日常联调或者跑回归你通常不想一直开着浏览器盯着曲线。Locust支持完全无界面运行适合丢给GitLab CI或Jenkins定时执行。locust -f locustfile.py --hosthttps://example.com --headless \ -u 200 -r 20 --run-time 5m --csvreports/result这个命令的意思是200个并发用户每秒生成20个持续压5分钟结束后把结果写到以result为前缀的CSV文件里。CSV文件会生成多个最常用的是result_stats.csv包含每秒钟的聚合指标以及result_failures.csv包含失败请求的具体信息。CI集成的时候还可以用--stop-timeout控制优雅停止时间用--exit-code-on-fail让压测在失败率超过阈值时退出非零状态。这样压测就可以作为发布流水线里的一个关卡如果新版本在约定的并发模型下失败率超预期流水线直接红掉发布流程自动拦截。这个能力在实际项目中比“压测报告发群里”有价值得多。5. 指标解读与常见问题排查压测跑完数据报表摆在那看不懂或者看错指标前面的功夫就白费了。这一节我重点讲怎么看指标以及实际跑压测时最容易踩的坑。5.1 关键报表指标RPS、响应时间与失败率Locust报表里最重要的几个指标我按重要程度排个序指标含义怎么看Request count总请求数确认压测确实产生到了预期的请求量级RPS每秒请求数衡量吞吐能力的主要指标Average response time平均响应时间只能做粗粒度参考容易被极端值拉偏Median response time中位数响应时间比平均值更能反映大多数请求的体验95th/99th percentile长尾响应时间真实用户体验的重要参考尤其适合发现瓶颈Failure count/%失败数量/比例任何非零失败都需要给出解释我见过不少人只盯着平均值看结果平均值漂亮得很P99却翻了10倍。这种“平均值陷阱”在压测里非常常见。真实用户不会只体验平均情况一个请求慢到10秒无论平均值多好看这个用户都很难受。所以在压测过程中我一般会把P95和P99作为核心关注对象平均值只用来做整体趋势判断。另一个关键点是RPS和并发用户数的关系。RPS不代表并发用户数它是“每秒完成的请求数”。同样的RPS如果每个请求响应时间从100ms变成500ms系统支撑的用户数会差很多。看压测结果时最好把RPS、并发用户数和响应时间三者放在一起看才能还原系统的真实承载情况。5.2 常见错误与定位思路跑压测一定会在某个时刻看到一屏红色失败请求。不要慌先看错误类型再决定怎么处理。ConnectionError连接无法建立通常意味着目标服务器的连接数达到上限或网络链路有问题。先用curl从压测机手动请求一下排除网络层误报。ConnectionResetError连接被服务端重置常见于服务端主动断连、网关超时、或者服务端配置了单连接请求数上限。HTTPError或者非2xx状态码响应返回了但状态码不对比如401/403/500。401通常是登录态失效去检查Token或Cookie逻辑500才是真正的服务端问题。Timeout等待响应超时。先看超时阈值设置得是否合理再看服务端慢查询、死锁等问题。我自己的排查习惯是先看失败时间段和系统监控时间轴是否对应再看是瞬间失败还是持续失败。如果是瞬间失败很多时候是连接池预热、缓存击穿之类的瞬时现象如果是持续失败那就更可能是代码逻辑问题或服务容量问题。另外不要把压测失败直接等同于服务不可用。有时候是压测脚本本身有Bug比如数据没参数化导致大量请求命中同一个不存在ID或者Token过期导致鉴权失败。在归因给“系统性能差”之前先确认脚本是否在真实模拟用户行为这一步能省下大量排查时间。5.3 压测过程中的节奏把控与监控习惯压测不是一键开跑一键停这么简单我习惯的节奏是先以目标并发的10%跑一遍确认基础链路和脚本没问题再逐步增加到50%、80%、100%每次跑3到5分钟观察指标变化。这个渐进加压的过程除了能保护目标系统更关键的是能帮你找到系统的性能拐点——到底是在哪个并发量级响应时间开始明显上扬这个数据比最终压到多少并发更有业务价值。压测期间我强烈建议同时监控目标服务器的CPU、内存、磁盘IO、网络带宽和数据库慢查询。没有这些系统侧数据遇到瓶颈时你只能猜。Locust能告诉你系统“变慢了”但它不告诉你为什么变慢——原因要结合系统监控才能定位。6. 几个值得记住的实践心得把这一路走下来的经验浓缩成几条希望你能少走我走过的弯路。第一把locustfile当正式代码维护而不是临时脚本。压测场景会随着业务迭代而频繁变化脚本放进Git仓库、写好注释、做Code Review长期收益非常高。用Python写脚本本身就是Locust最大的优势别把这个优势浪费成“一次性脚本工具”。第二压测开始前先明确本次要验证的问题是什么。是验证新接口的容量上限是验证数据库连接池配置是否合理还是验证发布新版本后有没有性能回退目标不同脚本设计和指标关注点都不同。没有目标的压测只会产出“看起来数字很高但谁也不知道说明什么”的报表。第三报告要留档而且要留下环境信息。同样一套脚本在测试机和预发布机上跑出来的结果可能差好几倍。报告里除了承载RPS和响应时间还要记录压测机配置、网络环境、目标服务的版本、数据库配置等上下文否则一个月后再看报告你会发现根本想不起来当时压的是什么环境。第四压测要尊重线上环境。如果目标系统是预发布或灰度环境请一定先和团队确认当前流量情况并设置好熔断或告警阈值。压测本身是为了发现系统的脆弱点不是为了把系统打挂后看热闹。负责任地使用工具是压测工程化的第一步。

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

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

免费获取报价 →
↑