资讯动态

FastAPI + Locust 接口压测实战:从搭建到瓶颈排查

发布时间:2026/9/9 2:41:47 来源:尧图企业网站定制
你有没有遇到过这种情况接口联调一切正常功能测试全部通过结果一上线就被用户投诉“系统好慢”。我见过太多项目栽就栽在上线前没做接口压测。很多人觉得压测是测试团队的事要么用 JMeter 配半天脚本要么干脆手动刷几个请求就当压过了。其实在 Python 技术栈里有一个组合能把接口压测这件事做得又快又专业用 FastAPI 写被测服务用 Locust 做压测工具。这篇文章就是我从 0 到 1 做一次完整压测的实战记录从搭建被测接口、编写压测脚本到执行压测、读懂报告、排查瓶颈整个过程都会讲清楚适合马上要上线但还没做过压测的后端开发也适合想引入轻量压测方案的测试工程师。1. 为什么选 FastAPI Locust这套组合能解决什么问题1.1 接口压测到底在压什么接口压测的字面意思是对接口施加压力但很多人的理解停留在“用工具发很多请求”这个层面。我更喜欢把它定义为在可控的并发压力下用数据量化系统行为。所谓系统行为包括吞吐能力每秒能处理多少请求、响应速度请求发出到收到响应的时间、稳定性长时间运行下错误率是否上升、内存是否泄漏、资源消耗CPU、内存、数据库连接等。这四个维度的数据才是上线决策的依据。做压测的目的不是为了证明“我的接口很快”而是为了在流量真正来临之前发现系统的能力边界和薄弱环节。举个最简单的例子一个登录接口单次请求只要 200 毫秒看起来很快但并发一上来数据库连接池如果只有 10 个连接200 个用户同时登录队列就会越排越长响应时间从 200 毫秒飙到 5 秒这时候你才知道系统真实的瓶颈在哪。还有一个常被忽略的点压测不只是上线前的一次性动作。每次发布大版本、每次数据库表结构变更、每次引入新的中间件都应该跑一遍回归压测把关键接口的性能基线记录下来后续版本和它对比性能回退就会立刻暴露。1.2 FastAPI 当被测服务贴近真实业务我选择 FastAPI 作为被测服务最重要的原因是它足够接近现在 Python 后端的真实形态。FastAPI 基于 ASGI 标准、原生支持异步性能在 Python Web 框架里属于第一梯队而且 starlette 加 pydantic 的组合意味着很多新项目愿意选它。拿它做压测目标得出的结论对真实业务有参考价值。另外一点很实际FastAPI 写起来比 Django、Flask 更简洁几分钟就能搭出一个包含同步接口、异步接口、参数校验、模拟数据库操作的小型服务。压测最忌讳的是被测服务过于简单比如只有一个返回字符串的 hello 接口这种服务压不出任何有意义的结论。我需要构造出有业务逻辑、有 IO 等待、有参数校验的接口FastAPI 能很方便地做到这一点。同时用 FastAPI 做被测服务还有一个好处压测过程中如果发现性能问题我可以直接在当前技术栈内做优化验证比如把同步接口改成异步、给模拟数据库操作加上连接池、调整 Uvicorn 的 worker 数量压测和优化的闭环可以完整在同一个服务上跑通这对于理解原理非常有帮助。1.3 Locust 当压测工具比 JMeter 更省心提到压测很多人第一反应是 JMeter它当然强大配置式操作对不了解代码的人友好但实际用到复杂场景时JMeter 的脚本理解和维护成本很高而且整套环境偏重。Locust 则是完全不同的思路压测场景就是普通 Python 代码用类和方法来描述用户行为天然支持编程式的逻辑组合。Locust 底层基于 gevent 协程一个进程可以模拟成千上万个用户压测机资源占用远小于线程型工具。这一点在压高频接口时尤其明显同样是模拟 1000 个并发用户JMeter 可能需要开不少线程对压测机本身的 CPU 和内存消耗很大Locust 则轻松得多。更关键的是Locust 自带一套 Web UI运行过程中能实时看到请求数、响应时间分布、失败率等图表数据是动态刷新的比 JMeter 跑完后看聚合报告更直观。当然我选择 Locust 还有一个团队协作层面的原因脚本是代码可以进 Git、可以做代码评审、可以复用函数和类。压测场景从简单到复杂是一个渐进迭代的过程用代码来管理这个过程比在 GUI 工具里拖拽组件要可控得多。后面的实战也会证明十几行 Python 就能写一个相当真实的用户行为模型。2. 搭一个能被压测的 FastAPI 服务环境、代码、启动全流程2.1 初始化项目依赖安装与目录结构开始之前先装环境。我建议用 Python 3.10 以上的版本本文所有代码基于 Python 3.10/3.11 验证新建一个项目目录然后创建虚拟环境再安装依赖。实际命令如下mkdir load-test-demo cd load-test-demo python -m venv venv source venv/bin/activate # Windows 下执行 venv\Scripts\activate pip install fastapi uvicorn pydantic这里 pydantic 会随 FastAPI 自动安装不用刻意指定版本。安装完成之后我的项目结构是这样的load-test-demo/ ├── main.py # FastAPI 被测服务 ├── locustfile.py # Locust 压测脚本 └── venv/ # 虚拟环境有些朋友习惯把压测脚本单独放一个目录比如tests/load/这当然可以。不过对于一次快速实战验证两个文件平铺放在根目录最简单后续要扩展再拆分也不迟。关键是main.py和locustfile.py要能独立运行互不依赖——压测脚本只关心被测服务的 HTTP 接口不关心服务内部实现。2.2 写接口同步、异步、带参数校验的三种典型接口下面是我为这次压测准备的完整main.py代码它包含了几种典型接口覆盖了日常后端开发中最常见的场景。from fastapi import FastAPI from pydantic import BaseModel import asyncio import time app FastAPI(titleLoad Test Demo API) # 模拟数据库存储 fake_items [] fake_posts [{id: i, title: fpost-{i}, author: demo} for i in range(500)] class ItemCreate(BaseModel): name: str price: float 0 tags: list[str] [] app.get(/) def read_root(): return {message: hello world} app.get(/posts) def list_posts(): 模拟列表查询 time.sleep(0.01) # 模拟轻微数据库查询耗时 return fake_posts app.get(/posts/{post_id}) def get_post(post_id: int): 模拟详情查询带路径参数校验 time.sleep(0.02) if post_id len(fake_posts): return {error: post not found} return fake_posts[post_id] app.post(/items) def create_item(item: ItemCreate): 模拟写入操作body 参数用 Pydantic 校验 fake_items.append(item) return {ok: True, id: len(fake_items)} app.get(/slow) def slow_operation(): 同步慢接口模拟阻塞耗时操作 time.sleep(0.5) return {message: sync slow done} app.get(/async-slow) async def async_slow_operation(): 异步慢接口模拟 IO 等待 await asyncio.sleep(0.5) return {message: async slow done}几个接口的设计意图解释一下。list_posts和get_post是典型的读接口做了简单的 sleep 来模拟数据库查询耗时。实际业务里一次列表查询可能涉及数据库慢查询、Redis 缓存未命中、下游服务调用耗时几十毫秒很常见所以这里用 10 到 20 毫秒的 sleep 来模拟比较合理。create_item是写接口用了 Pydantic 模型做参数校验压测时如果请求体格式不对接口会返回 422 状态码。这正好能用来验证 Locust 脚本里 post 的 payload 写没写对——如果失败率奇高且错误都是 422那不是被测服务的问题是压测脚本的问题。slow和async-slow则是我特意留作性能对比用的接口。它们模拟同样的 0.5 秒耗时前者是同步阻塞后者是异步等待。在同一个服务里压这两个接口你能非常直观地看到同步接口在 Uvicorn 单 worker 下的并发瓶颈这对理解后面压测结果非常有帮助。2.3 用 uvicorn 启动服务并验证接口接口写完后启动服务用 uvicorn 一行命令就行uvicorn main:app --host 0.0.0.0 --port 8000注意--reload只在本地开发调试时使用比如改完代码想立刻生效可以用uvicorn main:app --reload。压测之前一定要去掉 reload因为 reload 会用额外的 watch 进程监听文件变化对性能有干扰压测结果会不准确。启动之后先别急着写压测脚本用 curl 把每个接口都验证一遍确保状态码和响应体符合预期curl http://127.0.0.1:8000/ curl http://127.0.0.1:8000/posts/3 curl -X POST http://127.0.0.1:8000/items -H Content-Type: application/json -d {name:book,price:39.9}如果一切正常你会分别得到 JSON 响应内部没有 500 错误。这一步虽然简单但相当于给压测提供一个“对照组”保证被测服务本身是健康的。2.4 配置 workers 与热更新要注意什么Uvicorn 的 worker 数量直接影响服务能扛住的并发量我见过很多人在这个参数上随手一写。对于以 CPU 计算为主的接口建议 workers 数量等于服务器 CPU 核心数对于 IO 密集的接口可以适当多一点但也不要盲目加大因为每个 worker 都是独立进程进程间的连接池、内存都是独立的worker 太多反而会浪费资源。如果要在多 worker 下测试启动命令是uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4但这里有两个坑必须提醒。第一个坑--reload和--workers不能同时使用。Uvicorn 会直接报错退出因为 reload 模式下每个 worker 都需要自己重新加载代码设计上就不兼容。我建议的用法是本地开发用--reload单 worker压测和部署用--workers N。第二个坑多 worker 下如果接口内部使用了内存态的数据比如我这个 demo 里的fake_items列表请求是随机落到不同 worker 进程的每个进程的数据互相独立。压测时如果脚本里的业务逻辑依赖跨请求的数据一致就会莫名其妙出错。所以生产环境需要把状态放到数据库或缓存里压测环境也尽量模拟这种情况不然压出来的结果没有代表性。3. Locust 压测脚本怎么写从最小可用到真实业务场景3.1 先装 Locust 并搞懂 4 个核心概念安装 Locust 只需要一行命令pip install locust装好之后写脚本前先理解 Locust 的四个核心概念这对后面调试帮助很大。第一个是HttpUser它代表一类参与压测的用户也是你定义任务集的基类。每个模拟用户启动后Locust 会创建对应的HttpUser实例并通过它的client属性发 HTTP 请求。第二个是task装饰器它把下面的方法标记为压测任务。一个用户可以执行多个任务任务之间按照装饰器括号里的数字分配权重。第三个是wait_time代表用户执行完一个任务后、开始下一个任务前的等待时间。Locust 内置了between(a, b)工具函数会在 a 到 b 秒之间随机取值模拟真实用户的行为间隙。第四个是on_start方法每个用户启动时会执行一次常用来做登录、获取 token 等初始化动作可以把它理解为测试场景里的前置操作。3.2 最小压测脚本20 行代码跑通先写一个能跑通的最小脚本locustfile.pyfrom locust import HttpUser, task, between class DemoUser(HttpUser): wait_time between(1, 3) task def view_posts(self): self.client.get(/posts)这段代码的含义是模拟用户启动后每 1 到 3 秒执行一次任务每次任务就是向被测服务发送 GET 请求访问/posts接口。保存文件后在项目根目录执行locust -f locustfile.py --host http://127.0.0.1:8000然后在浏览器打开http://127.0.0.1:8089你会看到 Locust 的 Web 页面。填入用户总数和每秒生成的用户数即爬坡速率点击启动压测就开始了。就这么简单。运行之后你会发现 Locust 的 Web 界面上实时刷新着请求数、平均响应时间、当前 RPS、失败率等数据界面底部还会按请求类型分别列出/posts这个接口的响应时间分位数。第一次跑通这个流程你对 Locust 的使用就算入门了。3.3 真实场景建模登录、Token、读写混合最小脚本只是验证工具链路真实业务里的场景远比这复杂。以最常见的带登录态的 Web 后端为例一个真实用户的操作路径可能是登录拿 token带着 token 访问列表页然后点开某个详情偶尔提交一个表单。把这段用户旅程翻译成 Locust 脚本就是下面这个样子from locust import HttpUser, task, between class BlogUser(HttpUser): wait_time between(0.5, 3) token def on_start(self): 每个用户启动时的初始化操作登录拿 token resp self.client.post(/login, json{ username: demo, password: 123456 }) if resp.status_code 200: self.token resp.json().get(access_token) def get_headers(self): return {Authorization: fBearer {self.token}} task(5) def list_posts(self): self.client.get(/posts, headersself.get_headers()) task(3) def post_detail(self): self.client.get(/posts/1, headersself.get_headers()) task(2) def create_item(self): self.client.post( /items, json{name: load-test, price: 9.9, tags: [test]}, headersself.get_headers() )这段脚本里on_start用来模拟登录拿到 token 后存到实例属性里之后每个任务都在请求头带上 Authorization。真实的接口鉴权逻辑可能更复杂比如 token 会过期、需要刷新你可以在任务里做异常捕获遇到 401 就重新登录这已经是高级用法了前期不必强求。还有一点要注意在 demo 的main.py里我没有写/login接口如果你直接复制上面的脚本会得到 404。所以要么在 FastAPI 服务里补一个简单的登录接口要么把on_start里的登录逻辑注释掉直接给 token 赋个固定值。压测的首要目标是验证接口性能不要被无关的鉴权流程卡住。3.4 等待时间与任务权重怎么设才合理等待时间和任务权重这两个参数直接影响压测结果是否接近真实场景。先说任务权重。比如真实业务里列表页访问量远大于发帖量那list_posts任务的权重就应该远大于create_item。权重并不是严格的请求比例它只决定任务被选中的概率。如果list_posts权重是 5、create_item权重是 2用户执行下一个任务时Locust 按权重随机选一个方法所以最终请求比例会大致靠近这个数值但不是完全精确。再说等待时间。wait_time between(1, 3)代表用户在两次请求之间休息 1 到 3 秒这是模拟真实用户阅读页面、思考操作的时间。如果没有任何等待时间用户会以最快速度不断发请求这种“零思考时间”模式适合测系统最大吞吐能力但不适合模拟真实业务流量。我一般会把两种场景分开做容量评估时用短等待时间甚至 0 等待做贴近生产的稳定性测试时用更接近用户习惯的等待时间比如between(2, 8)。4. 完整执行一次压测并读懂结果命令、图表与指标4.1 Web UI 模式可视化操作与实时监控Locust 最吸引人的一点就是它的 Web UI。启动命令很简单locust -f locustfile.py --host http://127.0.0.1:8000启动后访问 8089 端口首页会让你填三个参数Number of users用户总数、Spawn rate每秒启动多少用户、Host被测地址通常在命令行指定后会自动填好。我在这里提醒一个关键操作技巧Spawn rate 不要一下子设成和用户总数一样大否则所有用户瞬间涌入容易造成启动阶段的瞬时连接风暴干扰你对系统稳定状态的判断。建议分阶段爬坡比如总共 200 个用户Spawn rate 设 20用 10 秒完成爬坡。如果你的目标是观察系统在压力逐渐上升过程中的表现甚至可以用更低的 Spawn rate让用户以斜线式增长观察系统从稳定到出现拐点的过程。Web UI 上的图表会实时更新重点看四个面板RPS 面板当前每秒请求数、响应时间面板平均和中位响应时间趋势、用户数面板模拟用户增长曲线、异常面板请求失败次数。如果异常面板开始出现红色错误说明系统已经接近甚至超过处理能力了。4.2 无头模式跑自动化压测参数逐个说清在 CI 流程里或者想跑一次无人值守的压测用无头模式更合适。一条典型的命令长这样locust -f locustfile.py --host http://127.0.0.1:8000 \ --headless -u 200 -r 20 -t 1m \ --csv loadtest_result \ --csv-full-history逐个解释参数参数说明示例值-u / --users并发用户总数200-r / --spawn-rate每秒生成用户数20-t / --run-time总运行时长1m / 10m / 1h--headless不开 Web UI--csv输出 CSV 结果的文件名前缀loadtest_result--csv-full-history输出按秒记录的历史数据--only-summary只打印最终汇总不打印逐秒记录注意-t的格式支持1m30s、1h这种组合写法比只用秒数更直观。另外无头模式跑完终端会打印一张汇总表格包含请求类型、请求数、失败数、中位响应时间、平均响应时间、P90、P95、P99、RPS 等数据。这张表很关键建议每次压测后都保存下来沉淀成性能基线。如果需要在压测结束后自动分析可以配合--csv导出的文件。Locust 会生成三个文件xxx_stats.csv请求统计、xxx_failures.csv错误详情、xxx_history.csv逐秒历史只有在--csv-full-history时生成。用 pandas 直接读stats.csv就能做数据分析和画趋势图。4.3 压测报告怎么读RPS、P90、P99、失败率很多新手拿到 Locust 报告只会看平均响应时间和 RPS这是不够的。我拿一次实际压测的汇总数据举例数据仅供参考主要用来说明怎么读并发用户RPS平均响应时间P50P90P99失败率1085118ms95ms160ms210ms0%50330152ms120ms245ms420ms0%100510196ms150ms350ms680ms0.2%200540380ms260ms780ms2100ms1.5%500545920ms480ms2100ms5300ms6.8%第一眼应该看的不是平均值而是 P99。平均值很容易被大量快速请求拉低就算 500 个并发时平均响应时间已经到了 920ms你还是不知道最差的那 1% 用户经历了什么。P99 是 2100ms 意味着有 1% 的请求要等超过 2 秒这在用户体验上已经是明显的卡顿。第二个要看的关键信息是系统拐点。上面这组数据里RPS 在 100 并发到 200 并发之间从 510 涨到 540涨幅很小但平均响应时间和 P99 却在急剧恶化失败率也开始出现。这就是典型的系统容量拐点吞吐已经接近上限再增加并发只会让响应时间进一步劣化。这个拐点对应的最大 RPS就是系统的容量基准。第三个要看的是失败率的分水岭。失败率从 0 到 0.2% 再到 1.5%、6.8%上升得非常快。这说明系统一旦越过容量拐点不只是变慢还会开始拒绝请求或连接超时。判断一个接口能不能扛住业务流量不能只看 RPS 高不高而是要看在目标并发下 P99 和失败率是否在可接受范围内。4.4 判断压测是否通过的参考标准压测“通过”没有统一标准每家业务的容忍度不一样但我可以给出一个比较通用的参考框架。先定业务目标比如活动大促时预计峰值 QPS 是 200线上接口 P99 要控制在 500ms 以内错误率不超过 0.1%。那压测的通过标准就是在 200 并发或更高一些比如 1.5 倍峰值下连续稳定运行一段时间P99 达标、失败率零增长。再定压测计划不要只跑一轮而是跑三档压力比如 50% 预期峰值、100% 预期峰值、150% 预期峰值每档跑 5 到 10 分钟记录 RPS、响应时间、资源占用。如果 100% 档稳定达标150% 档出现性能拐点说明系统有合理余量如果 100% 档就不达标那就需要定位优化压测的目的也就达到了。最后还要看稳定性短时间飙高不代表系统能持续运行。我习惯在通过阶梯压测后再跑一次 20 分钟左右的持续压力观察内存是否持续增长、RPS 是否随时间下降。很多内存泄漏和连接泄漏问题只有长时间压测才能暴露出来。5. 压测之后如何排查瓶颈并针对性优化5.1 从结果反向定位瓶颈的基本排查路径压测发现性能不达标时别急着改代码。先按下面的路径排查每一层都是一类常见瓶颈。先看被测服务所在机器的 CPU 和内存。压测过程中用top或htop观察如果是 CPU 跑满说明瓶颈在计算层优先查代码里的重计算、序列化、日志打印如果 CPU 没跑满但响应时间已经很高说明瓶颈大概率在 IO 层比如数据库查询、网络等待、磁盘读写。再看数据库连接数。FastAPI 服务每次请求要查询数据库通常用的是连接池。压测时如果数据库连接池被打满新请求就要排队等连接响应时间会上升得非常明显。这类问题在压测时经常表现为服务端 CPU 不高但接口响应时间很高日志里能看到数据库连接等待超时。最后看下游依赖。如果被测接口依赖 Redis、第三方 API 或消息队列压测时这些依赖同样会成为瓶颈。最简单的验证方法是压测时同时看这些中间件的监控指标或者临时把依赖替换成 mock对比替换前后接口耗时就能判断瓶颈是否在下游。5.2 同步接口和异步接口的压测对比我在 demo 里预留的/slow和/async-slow两个接口就是用来做这个对比的。它们都模拟 0.5 秒的耗时区别在于一个是time.sleep(0.5)阻塞当前进程一个是await asyncio.sleep(0.5)把控制权交还给事件循环。在单 worker 下同步接口在 50 个并发时就很吃力因为 Uvicorn 对同步接口默认在线程池里跑线程池大小有限默认 40 左右并发请求一旦超过这个数后面的请求就得排队。而异步接口在同样的并发下表现会好得多因为 0.5 秒的等待没有阻塞事件循环进程能同时处理大量请求。实测下来这两个接口的压测结果差异非常明显低频并发下二者差别不大一上并发异步接口的吞吐能高出好几倍。这个对比也说明了为什么现代 Python 后端强调写异步代码尤其对于 IO 密集型的业务把time.sleep换成await asyncio.sleep把同步 DB 驱动换成异步 DB 驱动性能提升立竿见影。5.3 三个立竿见影的优化动作压测发现瓶颈后如果你不确定从哪里下手先试这三个动作它们解决的是大多数 Web 服务最常见的性能问题。第一个动作是加 Uvicorn worker。单 worker 能扛的并发有限用--workers 4启动 4 个进程整体吞吐通常能接近线性提升注意是“接近”因为进程增多后数据库连接数、文件句柄数都会成倍增加不能无限加。加 worker 之前先确认代码没有进程内的状态依赖否则会出现数据不一致的问题。第二个动作是把同步代码改成异步。前面已经验证过IO 密集的接口从同步改异步并发能力提升非常明显。实际项目里如果数据库驱动不支持异步可以先在接口入口用run_in_threadpoolFastAPI 自带把阻塞调用放到线程池里执行也能缓解阻塞问题但要额外注意线程池大小上限。第三个动作是加缓存。如果压测中有大量重复请求比如热点数据查询用 Redis 或者进程内缓存扛住大部分读请求数据库压力会骤降响应时间也会大幅回落。这个优化动作对带状态的中间件的依赖较强压测前要确认缓存策略和真实业务一致否则压测数据也失去参考意义。6. 实战踩坑记录高频问题与排查技巧6.1 FastAPI 侧的问题清单第一个高频问题就是热更新不生效。网络上的 FastAPI 教学大多会告诉你用uvicorn main:app --reload启动但在某些环境里你会发现自己改了代码服务端并没有自动重载。原因通常有三个第一启动命令里漏写了--reload第二项目用了多 worker--reload和--workers不兼容第三Docker 或虚拟环境里文件监听机制不正常。排查时先去掉--workers加--reload并确认当前工作目录下main.py位置正确。第二个问题是压测过程中服务突然返回大量 500 错误但服务端日志没有明显异常。我遇到过的最常见原因是操作系统层的文件描述符限制。虽然 Uvicorn 能处理很多并发连接但如果你没有设置任何限制ulimit -n可能先成为瓶颈。压测前检查一下这个参数适当调大比如ulimit -n 65535能避免很多莫名其妙的连接失败。第三个问题是请求参数校验导致的失败。FastAPI 的 Pydantic 参数校验非常严格比如price字段定义了float类型压测脚本里传了字符串9.9就会返回 422。这种情况在压测失败率里会大量出现看起来像服务挂了其实是请求体不对。排查方法就是看 Locust 失败详情里的状态码如果是 422 而不是 500问题在压测脚本而不在被测服务。6.2 Locust 侧的问题清单Locust 这边最常见的坑我遇到过的有这么几个。第一个是压测机自身资源耗尽。虽然 Locust 基于协程很轻量但当用户数特别大比如上万且每个用户都有高频率任务时压测机本身的 CPU 和网络也会成为瓶颈。判断方法很简单压测机的 CPU 如果已经跑满RPS 还在抖动那压出的数据基本不可信。这时候应该启用分布式压测用一台主节点加多个 worker 节点分担压力。第二个是 TIME_WAIT 连接堆积。短连接模式下压测结束压测机的网络连接会大量进入 TIME_WAIT 状态如果端口被占满新连接就建立不起来导致压测失败率突然飙升。我一般会在压测机上调整系统的端口范围和 TIME_WAIT 复用参数或者让被测服务支持 keep-alive减少建连和断连的开销。第三个是on_start登录逻辑导致的连锁失败。如果登录接口本身扛不住压力那么每个新用户启动时登录失败后续所有带 token 的任务自然全部失败整个压测结果看起来就是一片红。这时候不要急着怀疑业务接口先看失败详情如果全是 401 或者登录接口的 5xx先把登录流程优化好或者降低用户生成速率。第四个是用户数从低到高爬坡时瞬间 RPS 过高。-r参数如果设置得太大大量用户同时在爬坡阶段发出第一个请求瞬时请求数会远高于稳定状态可能导致系统在压测刚启动时就崩溃。这不是系统的真实稳态表现而是启动冲击效应。解决办法是调低-r给系统一个缓冲爬坡过程。6.3 压测方法论上的几点提醒作为压测实践的最后补充分享几个我在真实项目里总结的方法论层面的经验。第一压测一定要在隔离环境进行。压测会产生大量数据写入如果你连的是开发库或测试库数据污染会干扰压测结果甚至影响其他同事的工作。我的习惯是压测前创建专门的压测数据库压测完成后清空数据保证环境可重复。第二压测结果要有可复现性。每次压测前记录代码版本、worker 数、数据库数据量、压测机配置、Locust 参数这些因素任何一个变化都会影响结果。没有记录的压测数据过两天回头看你根本不知道它代表什么。第三不要追求一次压测就通过。性能优化是一个循环压测、分析、优化、再压测。我第一次给一个 FastAPI 项目做压测时200 并发就把数据库连接池打爆了后来一步步优化异步代码、调整连接池参数、加缓存最终稳定扛住了 800 并发。这期间大概跑了十几轮压测每次只改动一个变量改了之后立刻验证效果。这种“一次只改一个变量”的节奏能让你准确知道每个优化手段的实际效果。第四也要留意压测的边界是“测到够用”而不是“把系统测死”。给自己定一个目标并发和目标延迟达到就可以收工。一味追求压到系统崩溃对业务价值有限反而浪费大量测试时间。最后说一点我个人的压测习惯。我现在每接一个 FastAPI 项目都会在上线前把关键接口的压测脚本写进仓库放在tests/load/目录下后续每次大版本发版都顺手跑一遍无头模式压测把 RPS 和 P99 记录到一个简单的表格里。几个版本下来你就能看到性能趋势哪些改动让接口变慢了、哪些优化让 RPS 提升了一目了然。压测不是上线前的仪式它是把系统性能从玄学变成数据的过程。这套 FastAPI Locust 的方案不依赖大型测试平台装好 Python 环境就能跑普通团队和个人项目都能用得上。如果你还没试过建议从今天的 demo 开始先跑通一条最简单的链路再慢慢加复杂场景很快你会发现以前那些“上线就卡”的惨案其实很多在压测阶段就能被拦住。

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

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

免费获取报价