要用好 Locust 做性能测试你需要的不是一堆“高级理论”而是先把这套工具从安装到跑通、再到看懂报告的全流程彻底吃透。这篇文章我不讲虚的直接用我在真实项目中踩过的坑和验证过的步骤带你把这个开源压测工具真正玩起来。先说句实在话如果你只是临时测个接口JMeter 的图形化界面确实上手快但一旦你的测试场景涉及复杂业务流、需要把压测脚本纳入 CI/CD 流水线、或者想用 Python 生态做数据分析Locust 的代码驱动模式就是碾压级的优势。这也是我用它替换掉部分 JMeter 场景的核心原因。1. 认识 Locust不只是个“压测工具”1.1 它到底解决什么问题Locust 是一个基于 Python 的开源负载测试工具。它的核心思想很有意思——不是用线程模拟并发而是用协程gevent。你可以把每个虚拟用户想象成一条在 Python 代码里“轻量级协程”它们以极低的资源消耗模拟成千上万用户同时访问你的系统。我给你一个直观对比同样一台 4 核 8G 的压测机器JMeter 用线程模拟 1000 并发可能 CPU 就飙到 80% 以上了而 Locust 用协程模拟同样数量的用户CPU 占用可能只有 30% 左右。这不是我瞎编的数据是我在同一套被测系统上实测过的差距。它能解决的问题包括接口级性能验证比如登录、查询、下单等核心接口的响应时间与成功率全链路场景压测用户从登录、浏览、加购物车到下单的完整流程模拟峰值容量摸底测出系统在什么并发量下开始出现性能拐点稳定性验证长时间如 8 小时持续加压观察系统是否存在内存泄漏或资源耗尽1.2 Locust 与 JMeter 的核心差异这个对比很多文章写过但我要从实际选型角度说点不一样的对比维度LocustJMeter脚本形式Python 代码XML 结构的 JMX 文件并发模型协程gevent单机可模拟高并发线程受限于线程数和 JVM 内存二次开发直接写 Python扩展成本极低需要写 Java 插件门槛高分布式内置 master/worker 模式支持动态扩缩需手工配置 agent稍显繁琐实时监控Web UI 图表 数据导出 API默认 GUI 图表分布式下监控较弱学习曲线需 Python 基础但也不深无需编程但高级场景反而更难你会发现一个真实规律如果只测简单 HTTP 请求JMeter 更容易上手但涉及复杂业务编排、需要自定义协议比如 WebSocket、或者要做压测数据二次分析时Locust 的 Python 灵活性和生态优势就拉满了。我个人的建议是团队里有 Python 基础的人直接选 Locust长期收益更高。1.3 适用场景与不适合的场景先说适合的Web 接口的性能回归测试尤其是 RESTful API涉及多步骤、有状态交互的业务流比如需要先登录、带 Token、再操作业务需要精细控制请求频率、思考时间的场景需要和 pytest、CI/CD 集成的自动化压测不太适合的纯 TCP/UDP 协议压测虽然可以通过自定义客户端实现但成本高于 netty 系工具需要录制回放浏览器操作这是 Selenium 系工具该干的事超大规模千万级并发压测——单机能力有上限分布式部署同样受限于网络和 coordinator 性能一句话总结Locust 是“程序员友好型”的压测工具它的核心价值在于把压测场景变成代码从而纳入工程化管理。2. 环境准备与安装全流程2.1 Python 环境准备含 Windows/macOS/Linux 差异Locust 是 Python 库所以先得有 Python 环境。我推荐 Python 3.9 以上版本因为新版本 Locust 对 Python 3.8 的兼容性有些小问题虽然官方文档说 3.8 都支持但实测 3.8 在部分 Linux 发行版上安装 gevent 会出现编译报错。如果你本机没装 Python建议先去 python.org 下载对应系统版本。这里说一下三个平台的注意点Windows安装时务必勾选“Add Python to PATH”不然安装完没法直接用 python 命令如果没有勾选需要在安装目录下找 python.exe手动配置环境变量重点Windows 上安装 gevent 原生扩展 c扩展理论上需要 Microsoft C Build Tools否则可能编译失败。但现在从 PyPI 下载的 gevent 通常有预编译 wheel 包直接 pip install 一般没问题macOS推荐用 Homebrew 安装brew install python3.11也可以直接装官方 pkg 包注意选择 x86_64 或 arm64 版本M1/M2 芯片选择 apple silicon 版本LinuxUbuntu/Debian 系sudo apt update sudo apt install python3 python3-pip python3-venv2.2 使用虚拟环境隔离依赖这里我要特别强调一下虚拟环境。我见过太多人在全局环境里直接 pip install locust结果过几个月另一个项目需要装某个依赖库版本冲突直接把 Locust 的依赖搞挂了整个压测环境报废。正确做法是创建独立的虚拟环境# 创建一个项目目录 mkdir locust-test cd locust-test # 创建并激活虚拟环境 python3 -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate激活后终端命令行前面会出现 (venv) 前缀说明你现在处于虚拟环境内所有 pip 安装的包都与系统隔离。从这一步开始我的所有命令都在虚拟环境下执行你可以从每个压测项目的依赖彻底隔离不用担心环境污染问题。2.3 正式安装 Locust 及依赖验证激活虚拟环境后直接执行pip install locust正常情况下它会自动安装 locust、gevent、flask、requests、pyzmq 等一票依赖。装完看一眼版本locust --version输出类似locust 2.31.2 from ...就表示安装成功。如果版本低于 2.0强烈建议升级到 2.x 甚至最新版因为老版本在 Web UI 和数据统计上差距很大pip install --upgrade locust我遇到过一种情况在 Python 3.12 上安装旧版 locust 时会报ModuleNotFoundError: No module named lib2to3这是因为新版 Python 移除了 lib2to3 模块而某些旧版 gevent 还依赖它。解决办法就是升级到新版 locust它会自动选择兼容新 Python 的 gevent。所以这里统一建议Python 3.12 最新版 Locust组合最稳妥。3. 拆穿“并发模型”的底层逻辑3.1 协程 vs 线程为什么 Locust 这么轻这是理解 Locust 性能底子的关键值得多说几句。传统压测工具如 JMeter使用线程模拟并发用户每个线程对应一个用户操作系统需要为每个线程分配独立的栈空间、保存上下文。如果你的目标是 10000 并发就需要创建 10000 个线程线程切换的开销和内存占用都非常吓人。Locust 基于 gevent 协程一个进程内可以有成千上万个协程并发执行协程切换是在用户态完成的不涉及内核上下文切换因此资源消耗极低。所以同一台机器上Locust 能模拟的用户数远高于线程模型。但这不代表 Locust 是万能的协程场景的前提是任务大多涉及 I/O 等待比如发 HTTP 请求等待响应、查数据库等待返回。协程恰好利用这些等待时间去调度别的协程从而把 CPU 和网络资源用满。如果你的测试代码里有大量 CPU 密集型计算比如频繁做加解密协程优势会被削弱甚至可能 GIL全局解释器锁还会限制多核利用率。3.2 Locust 的请求架构与 gevent 协作细看 Locust 的请求执行链虚拟用户继承 User 类在 while 循环中执行任务每个任务通过self.client.get(...)发起 HTTP 请求请求实际上由 gevent 协程池管理等待响应时协程让出控制权事件循环处理其他协程任务直到响应回来再恢复该协程这种模型下代码看起来是同步写法的但执行却是异步并发的。你能用自然的 Python 语法写“登录 → 查询 → 下单”这种有先后依赖的业务流而不用像 Node.js 那样写 callback 或 promise 链。3.3 单机 Locust 的并发上限评估根据我实测的经验给个粗略参考机器规格目标接口平均响应时间可模拟并发量级2 核 4G50ms 以内2000-30004 核 8G100ms 左右3000-50008 核 16G200ms 左右5000-10000注意这是纯 HTTP 请求、无复杂计算、无大量外部系统调用的理想情况。实际项目里如果每个虚拟用户每秒都要对被测系统产生多次请求压测机的资源消耗会明显上升。如果你的目标并发超过单机能力就需要走分布式模式后面专门讲。4. 安装完先跑一个最简单的 Demo4.1 编写你的第一个 locustfile.py先在项目目录下创建一个文件命名locustfile.py。Locust 启动时会默认在当前目录找这个名字如果你用其他文件名启动时要加-f参数指定。下面是最基础的入门脚本from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time between(0.5, 2) task def load_homepage(self): self.client.get(/)就这么 8 行。我来逐行解释HttpUser表示一个虚拟用户它能直接使用self.client发起 HTTP 请求wait_time between(0.5, 2)每个用户执行完一个任务后随机等待 0.5 到 2 秒再执行下一个任务模拟真实用户在页面间的思考时间task把下面的方法标记为压测任务Locust 会循环调用它self.client.get(/)向目标主机发起 GET 请求注意这个脚本里并没有定义 host我们需要在启动命令里指定目标地址。4.2 启动 Locust 并用 Web 界面压测运行locust --host http://example.com其中http://example.com换成你的被测服务地址。启动成功后终端会显示Web UI is now available at http://0.0.0.0:8089打开浏览器访问http://localhost:8089你会看到填写并发数和 spawn rate 的界面。假设配置Number of users (peak concurrency) 100Spawn rate (users started/second) 10然后点击 “Start swarming”就会看到实时统计界面。这里我建议你先用http://localhost:8089做一次 quick test确认整个链路通了再开始做真正的压测。4.3 无界面模式跑压测并输出结果Web 界面适合调参和观察。如果你要用于自动化或无人值守场景用命令行无界面模式更合适locust -f locustfile.py --host http://example.com --headless -u 100 -r 10 -t 60s参数解释--headless无 UI 模式直接开始施压-u 100模拟 100 个虚拟用户-r 10每秒启动 10 个用户-t 60s总压测时长 60 秒命令结束后终端会打印汇总报告包含总请求数、失败数、RPS、响应时间中位数与百分位95%、99% 等。配合 CSV 输出可以留给后续分析:locust -f locustfile.py --host http://example.com --headless -u 100 -r 10 -t 60s --csvresult --csv-full-history运行完会生成result_stats.csv和result_stats_history.csv其中 history 文件记录每秒的采样数据适合画趋势图。5. 核心配置与高级玩法让压测脚本更贴近真实场景5.1 wait_time 的三种配置方式和适用场景刚才用了between但 wait_time 还有两种更精准的写法from locust import HttpUser, task, between, constant, constant_pacing class WebsiteUser(HttpUser): # 方式 1固定等待 1 秒 wait_time constant(1) # 方式 2在 1 到 3 秒之间随机 wait_time between(1, 3) # 方式 3恒定请求频率每 2 秒完成一次请求包含请求执行时间 # wait_time constant_pacing(2)我的建议模拟用户浏览行为用between最自然模拟固定心跳类探活用constant模拟“固定 QPS”的压测模式用constant_pacing。5.2 多任务权重设置真实系统里不同接口的调用频率是不一样的。比如查询接口占 80%、下单占 10%、支付占 10%。用权重就可以模拟这种分布class WebsiteUser(HttpUser): wait_time between(0.5, 2) task(8) def view_products(self): self.client.get(/products) task(1) def add_to_cart(self): self.client.post(/cart, json{product_id: 123}) task(1) def checkout(self): self.client.post(/order, json{cart_id: 456})权重数字越大被选中的概率越高。Locust 内部会根据权重占比调度任务最终总请求分布大致符合设定比例。5.3 on_start 方法处理登录、Token 初始化性能测试的流程往往不是“裸请求”而是需要先登录拿 token。Locust 提供了on_start方法每个虚拟用户启动时会自动执行一次class WebsiteUser(HttpUser): wait_time between(0.5, 2) token None def on_start(self): resp self.client.post(/login, json{username: test, password: 123456}) data resp.json() self.token data[access_token] # 给后续请求统一带上 Authorization 头 self.client.headers.update({Authorization: fBearer {self.token}}) task def get_profile(self): self.client.get(/profile)这里有一个关键点on_start是协程环境下执行的每个虚拟用户都会独立登录一次。如果登录接口本身就有性能瓶颈这部分开销会计入压测数据。所以需要考虑测试的目标是“包含登录的完整链路”还是“仅业务接口”。在纯业务接口性能测试场景更好的方式是在外部先批量拿到 token然后通过参数注入给每个虚拟用户使用避免压测目标被登录接口污染。5.4 HttpUser 与 FastHttpUser 的选择Locust 的默认客户端是基于 requests 库的 HttpUser功能全、API 友好。但 requests 底层是 urllib3连接复用和性能不如 gevent 原生的协程化实现。所以当压测目标是追求高吞吐、高并发时建议改用FastHttpUserfrom locust import FastHttpUser, task, between class WebsiteUser(FastHttpUser): wait_time between(0.5, 2) task def load_homepage(self): self.client.get(/)FastHttpUser 基于 geventhttpclient在大量小请求场景下性能明显优于 HttpUser。不过它也有代价self.client返回的 Response 对象 API 和 requests 有差异部分方法不可用。比如你需要获取响应 JSON 并解析里面的某些字段时可以通过resp.json()但要注意它的实现是基于流式解析的。我自己的实践经验是常规接口压测先用 HttpUser确保脚本正确正式大规模压测时再切到 FastHttpUser用相同的代码逻辑对比一下吞吐差异往往能差出 30% 以上。5.5 参数化与 CSV 文件驱动测试数据真实压测中每个用户需要不同的数据不然可能因为数据竞争导致测试结果失真。Locust 本身不提供参数化的内置功能但你可以用 Python 的随机选择实现import random import pandas as pd users pd.read_csv(test_users.csv).to_dict(records) class WebsiteUser(HttpUser): wait_time between(1, 3) def on_start(self): user random.choice(users) self.username user[username] self.password user[password] resp self.client.post(/login, json{ username: self.username, password: self.password, }) task def get_info(self): self.client.get(f/user/{self.username}/info)这里我建议用 pandas 读取数据是因为它处理大文件更方便如果数据量不大直接用 csv 模块也完全 OK。注意random.choice是线程安全的但在协程环境下反复调用没问题。如果是海量数据比如 10 万用户更推荐把数据读进内存后用random.choice或random.randint索引访问避免频繁读文件造成额外开销。5.6 TaskSet 组织复杂业务流程当场景复杂到有多个阶段、且不同阶段有先后顺序时用 TaskSet任务集来组织更清晰from locust import HttpUser, task, between, TaskSet class LoginTasks(TaskSet): def on_start(self): self.client.post(/login, json{username: u1, password: p1}) task(5) def browse(self): self.client.get(/products) task(1) def logout(self): self.client.post(/logout) self.interrupt() # 退出当前 TaskSet class WebsiteUser(HttpUser): tasks [LoginTasks] wait_time between(1, 5)self.interrupt()是 TaskSet 特有的机制它表示“当前任务集执行完毕退出并回到用户主循环”允许用户重新选择下一个 TaskSet。6. Web UI 性能指标深度解读启动 Web UI 后你会看到一堆数字很多新手会直接盯着 RPS 看这是不对的。我这里把核心指标讲透。6.1 核心指标的真正含义指标含义判别标准RPS每秒请求数吞吐量即系统每秒能处理多少请求越高越好但要结合响应时间看响应时间中位数50% 请求的响应时间低于这个值反映社区大部分用户的体验响应时间 95%95% 请求的响应时间低于这个值反映系统在大部分情况下的上限响应时间 99%99% 请求的响应时间低于这个值反映长尾延迟影响极端用户体验失败率请求失败占比一般应低于 0.1%某些场景可放宽用户数当前并发用户数与压测设定一致我特别强调一下在性能测试中中位数和平均数都会被极端值带偏真正能帮助你判断体验质量的是 95% 和 99% 这两个百分位。如果中位数是 50ms但 99% 是 3s说明大部分用户很流畅但每 100 个人里就有 1 个人体验极差这种长尾问题在接口性能上往往意味着存在某种偶发的阻塞。6.2 观察“响应时间随并发变化”的拐点压测的核心目标之一就是找出系统的性能拐点也就是系统并发能力从线性增长转为下降的临界点。观察方法固定并发在 50观察 RPS 和响应时间的稳定值并发提升到 100对比 RPS 是否按比例提升继续提升到 200、400观察 RPS 增长是否变缓、响应时间是否突然巨幅上涨如果并发 200 → 400 后RPS 没有成倍增长反而出现大幅波动说明已经到了瓶劲位置需要结合服务端监控进一步定位。6.3 关于“每个用户每秒产生多少请求”的估算这里有人会问我设定 100 个虚拟用户但 RPS 显示只有 50 左右正常吗完全正常。因为每个虚拟用户执行一次任务后要等待wait_time指定的思考时间假设每个任务本身耗时 0.2 秒思考时间是 1 到 3 秒平均 2 秒那每个用户的平均请求频率是 1/(0.2 2) ≈ 0.45 次/秒100 个用户总 RPS 在 45 左右是符合预期的。另一种估算方法RPS 并发用户数 / 单请求平均响应时间。比如 100 并发、平均响应时间 1 秒那么 RPS 大约 100。如果你发现 RPS 明显低于这个值可以看看是不是 wait_time 设置太长把整体吞吐拉低了。7. 分布式压测突破单机瓶颈的实战方案7.1 什么时候必须用分布式当单机 Locust 无法满足目标并发量或者压测机 CPU/网络成为瓶颈时就需要分布式模式。典型场景目标并发超过 5000且单台压测机 CPU 已高负荷压测场景包含大量静态资源下载或文件上传网络带宽成为瓶颈被测服务部署在多地域需要从不同地点同时发起流量7.2 Master-Worker 架构与启动方式Locust 的分布式模型是一台 master 负责调度和收集数据若干 worker 负责实际施压。启动方式先启动 masterlocust -f locustfile.py --host http://example.com --master再启动 worker在另外的机器或同一台机器的其他终端locust -f locustfile.py --host http://example.com --workermaster 默认监听 5557 端口用于 worker 通信和 5558 端口用于数据同步。worker 启动时会尝试连接 master 的 5557 端口。如果 worker 不在同一台机器需要指定 master 的 IPlocust -f locustfile.py --host http://example.com --worker --master-host192.168.1.1007.3 动态扩缩容的实操技巧分布式模式最巴适的一点是worker 启动后可以随时加入或退出不需要重启 master。这意味着先用 10 台 worker 跑基线压测通过 Web UI 观察总 RPS 和每台 worker 的负载均衡情况如果不够再启动更多 workerWeb UI 会实时体现新增数据这里有个实操细节master 本身不发请求真正施压的只有 worker。所以压测机的资源评估要按 worker 的机器规格来算master 用的机器可以稍微弱一点比如 2 核 4G 足够。另外注意所有 worker 上的 locustfile.py 必须完全一致包括任务定义和权重。如果不同 worker 脚本不一致会导致测试结果无意义。7.4 分布式运行时的风险提醒分布式不是银弹有几个现实问题必须先想清楚每台 worker 都是一个独立进程它们各自维护随机数种子因此每台的用户分布比例大致相同但统计上可能略有起伏如果被测系统有 IP 维度的限流或 WAF 防护分布式压测会同时触发更多不同来源 IP可能导致误伤或提前触发防护机制master 和 worker 之间的网络延迟如果过高会影响数据收集实时性一般要求在同一内网环境1000 并发以下完全不需要分布式单机 FastHttpUser 足够8. 在真实业务中常用的进阶配置与扩展8.1 自定义负载形状Load Shape按时间自动调节真实业务不是恒定并发的比如电商大促时的用户量会先快速攀升、维持峰值、再慢慢回落。Locust 2.x 提供了LoadTestShape类可以自定义负载变化from locust import HttpUser, task, between, LoadTestShape class StepLoadShape(LoadTestShape): def tick(self): run_time self.get_run_time() if run_time 60: # 第一阶段每秒启动 5 个用户目标 200 并发 user_count 5 * run_time elif run_time 120: # 第二阶段稳定在 300 并发 user_count 300 elif run_time 180: # 第三阶段回落到 100 user_count 100 else: return None # 返回 None 表示结束压测 spawn_rate 10 return (user_count, spawn_rate) class WebsiteUser(HttpUser): wait_time between(0.5, 2) task def load_homepage(self): self.client.get(/)运行时直接locust -f locustfile.py --host http://example.comtick()方法返回的元组表示当前应该达到的用户数和 spawn rate返回 None 即停止测试。这种方式比用-u固定并发灵活太多了而且完全用代码控制方便纳入版本管理。8.2 事件钩子压测中执行自定义逻辑你可能会想在压测过程中记录一些额外指标比如第三方服务调用耗时、消息队列积压量、数据库连接池大小。Locust 提供了事件钩子from locust import events events.test_start.add_listener def on_test_start(environment, **kwargs): print( 压测开始 ) # 这里可以发送通知或初始化监控系统 events.request.add_listener def on_request(request_type, name, response_time, response_length, exception, **kwargs): if exception: print(f请求失败: {name}, 异常: {exception}) else: # 记录耗时超过 2s 的请求 if response_time 2000: print(f慢请求: {name}, 耗时: {response_time}ms)request事件在每次 HTTP 请求完成后触发参数包含请求类型、名称、响应时间、响应长度、异常等。你可以在里面做些自定义统计比如把数据推送到监控面板。8.3 与 CI/CD 集成压测作为质量门禁把 Locust 挂在 CI 流水线里是提升工程质量的很有效的手段。基本思路代码变更后自动触发短时压测1-2 分钟校验关键指标是否达标不达标就阻断发布。我用 GitHub Actions 做过类似实践name: performance-test on: pull_request: branches: [main] jobs: load-test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Setup Python uses: actions/setup-pythonv4 with: python-version: 3.11 - name: Install dependencies run: pip install locust - name: Run load test run: | locust -f locustfile.py \ --host ${{ secrets.STAGING_URL }} \ --headless -u 50 -r 10 -t 60s \ --csvreport --csv-full-history - name: Check threshold run: | python check_report.pycheck_report.py可读取生成的 CSV判断失败率和 P95 是否在阈值内import csv threshold_failure_rate 0.01 # 失败率不超过 1% threshold_p95 800 # P95 响应时间不超过 800ms with open(report_stats.csv, encodingutf-8) as f: reader csv.DictReader(f) row next(reader) failure_rate float(row[Failure Percentage]) p95 int(float(row[ 95%])) if row.get( 95%) else 0 assert failure_rate threshold_failure_rate, f失败率超标: {failure_rate} assert p95 threshold_p95, fP95 超标: {p95}ms print(f压测通过: 失败率 {failure_rate:.2%}, P95 {p95}ms)一个小小的避坑提示CI 环境的资源通常有限容易造成压测机本身成为瓶颈导致结果不准确。因此 CI 里的压测建议做短时轻量验证专业全量压测还是放到独立压测环境。8.4 压测数据的 CSV 输出与二次分析刚才提到的--csv参数会生成几个文件result_stats.csv整个压测周期的汇总统计result_stats_history.csv按秒采样的历史数据适合画曲线图result_failures.csv失败请求明细result_exceptions.csv异常明细我通常会用 history 文件结合 pandas 画吞吐量-响应时间曲线直观看出系统在并发攀升过程中的表现import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(result_stats_history.csv) df df[df[User Count] 0] fig, ax1 plt.subplots() color tab:red ax1.set_xlabel(Time (s)) ax1.set_ylabel(RPS, colorcolor) ax1.plot(df[Timestamp], df[Total Request Count], colorcolor) ax1.tick_params(axisy, labelcolorcolor) ax2 ax1.twinx() color tab:blue ax2.set_ylabel(P95 (ms), colorcolor) ax2.plot(df[Timestamp], df[95%], colorcolor) ax2.tick_params(axisy, labelcolorcolor) plt.title(RPS vs P95) plt.savefig(performance_curve.png)这种图表放到汇报材料里比讲一堆数字有说服力得多。9. 常见问题排查与避坑指南9.1 经典问题速查表这里我汇总了多次实战中踩过的坑和官方群里高频问题常见问题可能原因解决方案FATAL ERROR: Failed to connect to masterworker 连不上 master检查 master 的 IP/端口防火墙确认 5557 端口开放压测结果里 RPS 为 0脚本里请求路径错误或 host 未指定先用 curl 验证接口可访问再用--host参数指定地址响应时间异常高但服务端负载很低压测机资源瓶颈或网络延迟检查压测机 CPU/内存/网络必要时用 FastHttpUser 或分布式内存持续增长最终 OOM脚本中有内存泄漏比如全局列表无限追加检查 on_start 里的数据累积避免在虚拟用户类中定义可变全局变量Web UI 打不开Locust 服务没启动成功或端口被占用看启动日志换端口--web-port 8088ConnectionError大面积报错目标服务的文件描述符/连接数限制调整服务端 ulimit或者检查连接池设置macOS 下安装报 gevent 编译错误缺少编译工具链安装 Xcode Command Line Toolsxcode-select --install压测启动后所有请求都失败host 填错、接口路径不对、依赖登录态用curl逐个验证检查on_start是否成功登录9.2 压测时发现失败率突然飙升怎么定位这是一个典型场景压测 30 分钟后失败率从 0 突然飙升到 30%。我从真实故障中提炼的排查路径先看失败请求的错误类型是超时、连接拒绝、还是 HTTP 5xx若是超时查看服务端的 GC 日志、慢 SQL、线程池队列情况若是连接拒绝检查服务端 TCP backlog 是否被占满若是 5xx检查服务端错误日志重点看是否有资源耗尽如连接池、内存另外要注意区分压测机导致的假失败。如果压测机自身 CPU 达到 100%请求会大量超时但这并非被测系统的性能问题。稳妥的做法是用topLinux或资源监视器Windows观察压测机状态一旦发现压测机成为瓶颈马上改用分布式或减少并发。9.3 关于测试数据清理与隔离压测会产生大量脏数据注册的用户、创建的订单、写入的日志如果不做数据清理连续压测几轮后数据库可能被塞爆影响后续测试结果。我的两个习惯独立测试环境永远不要直接在生产环境做全量压测除非你是在做生产全链路压测且有完备保障压测前跑一遍数据清理脚本压测后也跑一遍保持环境干净还有一个细节如果被测系统对同一手机号或账号有唯一性约束压测脚本里要用随机数据拼接比如test_user_{random.randint(10000, 99999)}example.com避免多个虚拟用户因账号冲突而报错。9.4 规避“压测脚本本身影响结果”的隐性因素最后分享一个容易被忽略的原则压测脚本要尽量“轻”。不要在虚拟用户的请求路径里加不必要的耗时代码比如频繁打印日志、读取超大文件、执行复杂字符串解析。这些操作会占用压测机的 CPU 和内存导致压测机先撑不住从而让被测系统的真实性能被掩盖。另一个高频错误是在self.client.get()里传入超时时间设置得过小导致网络抖动时误判失败。建议超时时间设为服务端 P95 响应时间的 5 倍左右比如 P95 是 200ms那么客户端超时设置 1000ms 以上比较合理。还有一个容易被忽略的细节压测机的 ulimit文件描述符限制会影响并发连接数Linux 默认 1024 很容易成为瓶颈建议提前调大ulimit -n 65535可以将它写入压测机的/etc/security/limits.conf这样每次登录后都会自动生效。10. 把它用到你的项目里一份可直接套用的执行清单如果你正准备开始一个项目的性能压测我建议按以下顺序推进确认目标这次压测要回答什么问题支撑多少并发发现哪些瓶颈验证某项优化是否有效准备环境独立的压测环境、压测机和被测服务的监控账号编写脚本先用 httpx 或 curl 手工验证核心链路再写 locustfile小规模试压5-10 个用户先跑 30 秒确认脚本无报错、指标收集正常正式压测根据目标逐步提升并发每档稳定运行 1-2 分钟记录数据收集监控数据服务端的 CPU、内存、GC、SQL 慢查询、消息队列积压量等分析瓶颈与调优针对性优化后再压一轮验证效果输出报告图表、结论、改进建议这里有个个人经验一次性能测试至少要有“基准测试”“压力测试”“稳定性测试”三个维度。基准测试是最小并发如 1-5 并发确认单用户性能基线压力测试逐步递增找拐点稳定性测试用 70% 左右的峰值并发持续运行 2-4 小时观察有无内存泄漏或性能退化。按照这个流程跑下来几乎可以肯定你能比大多数团队更早发现线上隐患。最后分享一个我个人的使用习惯我会在压测脚本中加入events.test_stop.add_listener钩子在压测结束时自动把摘要数据推送到团队的消息群里。这样压测一结束相关同事立刻就能看到结果省去了人工转发报告的环节。如果你团队用企业微信或钉钉Webhook 推送写起来也就是几行 requests.post 的事。性能测试不是一次性的活动把它纳入日常工程习惯里价值才会持续释放。