
如果你最近还在用 ab、wrk 这类老牌压测工具去撑高并发场景我建议你先停一下。不是它们不能用而是在面对现在的分布式系统、微服务网关、AI Agent 这类带状态、带动态参数的业务时传统的压测工具已经开始力不从心。最近我一直在折腾 Locust并且把 AI 智能体这套东西接进去做用户行为模拟实测下来效果非常夸张一台压力机可以轻松撑起几万并发配合分布式部署模拟 10 万并发用户行为不再是一句口号。这篇文章就把我这几周从踩坑到落地的经验完整写出来包括工具选型、脚本设计、分布式部署、AI 智能体怎么接入以及那些文档里根本不会写的问题。先说结论我不建议你用 ab 去测现代 Web 应用更不建议在 2025 年还抱着“压测就是不断发请求”的思维。真实的用户行为是带状态的是有思考时间的是有业务链路的。Locust 能火起来恰恰是因为它用 Python 脚本描述用户行为又靠着协程把并发能力拉满。而当 AI 智能体开始介入它带来的不只是脚本生成效率的提升更是对“用户行为建模”这层逻辑的重新定义。这篇文章希望帮你搞清楚AI 时代做压测到底应该怎么玩。1. 压测工具的“变天”逻辑为什么 ab 不够用了1.1 ab 的优势和它真正的“年龄段”我们得先说句公道话。Apache abApacheBench当年能火是因为它太简单了一条命令就能打出一个静态页面的每秒请求数输出结果里有每秒请求数、平均响应时间、百分位延迟对当年的服务端性能评估来说完全够用。那时候的应用大多是单体架构接口是静态路径压测的本质确实就是“循环发请求”。但现在的业务早就不是这样了。你打开一个电商 App一个普通用户进入首页可能触发几十个接口请求先拿配置、再拿用户信息、拉取推荐列表、上传埋点、查询优惠券……这还是在没有登录状态的情况下。ab 没有办法描述这种“用户行为流”它只会并发执行同一条 URL。你要是拿着 ab 往带鉴权的接口上打光处理 401 和 token 过期就能让你怀疑人生。ab 的第二大问题是它基于单进程单线程模型。虽然可以通过多个进程去并发但本质上它是一个非常“憨”的工具没有用户思考时间没有请求间依赖没有动态参数提取。你可以把它理解为一条水管直接往服务器灌水遇到的问题就是“它只能测这条水管的最大流量”却测不出真实用户的体感。1.2 现代压测至少需要具备哪些能力我在实际做压测方案时对工具的要求基本有五条能描述多步骤用户行为而不是单条 URL 请求。支持动态参数传递比如从登录接口拿到 Token再带到后续请求头里。能模拟用户思考时间、浏览路径而不是无脑死循环请求。支持分布式压测一台机器干不动的时候可以平滑横向扩容。最好能用代码维护场景脚本脚本能进 Git能 code review。这五条一列出来ab 就被淘汰了。就算你用 ab 加 shell 脚本包裹各种逻辑写出来的东西维护成本极高而且每加一个场景就要改一遍 shell根本追不上业务迭代速度。而 Locust 天生就是为解决这些问题而生的。1.3 Locust 的差异化定位Python 脚本 协程并发Locust 的底层架构非常有意思。它不是用线程去模拟用户而是用 gevent 提供的协程greenlet。协程的创建成本比线程低得多内存占用也小很多。一个线程默认栈大小是 1MB 到 8MB而一个 greenlet 协程的开销只有几 KB。这意味着同一台 8C16G 的压力机上普通工具用线程模拟 5000 用户可能就开始频繁切换上下文了而 Locust 采用协程模型能够轻松模拟数万用户。这里要强调一下Locust 里说的“用户数”不是一个真实线程而是一个协程任务循环。每个用户实例在循环里执行你定义好的任务执行完一个请求之后 wait_time 会控制它思考多久再进入下一个任务。正因为协程足够轻量所以 Locust 能以相对低的硬件成本模拟高并发。而且 Locust 用 Python 脚本定义用户行为这给它带来了巨大的灵活性。一个 HttpUser 类就是一个用户模型你在类里边定义任务列表每个任务是一个 Python 方法方法里写接口请求、解析响应、提取变量。整个脚本就是一个标准的 Python 文件天然支持版本管理、单元测试、CI/CD 集成。这对 AI 智能体的接超级友好因为大模型最擅长的输出格式就是代码而 Locust 的场景脚本本身就是代码。2. Locust 核心机制拆解从脚本到协程调度2.1 最小可用脚本里到底发生了什么一个最简单的 Locust 脚本长这样from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time between(1, 5) task def index_page(self): self.client.get(/)这个脚本看起来平平无奇但它内部包含了一个压测脚本需要的全部核心概念HttpUser类定义了“一种用户”。wait_time控制用户执行完任务后的思考时间模拟真实用户点击页面之间的间隔。task装饰的方法代表用户会执行的行为多个任务之间可以设置权重。self.client是底层基于 requests 库封装的 HTTP 客户端支持 GET、POST、PUT、DELETE 等所有常见方法。你启动 Locust 后调度器会根据并发用户数创建对应数量的协程。每个协程就是一个独立的用户它会在一个死循环里不断执行任务。执行完一个任务后先 sleep 一段 wait_time 指定的时间再继续下一个任务。2.2 HttpUser 与 task 的进阶玩法真正到了复杂业务场景光一个index_page是远远不够的。我实际项目里的脚本会把用户行为拆成多个任务并且给每个任务设置权重模拟真实用户的行为分布。比如一个电商场景可能 60% 的用户去浏览商品列表30% 的用户查看详情10% 的用户加购物车并结算。代码写出来就是这样from locust import HttpUser, task, between import random class ShopUser(HttpUser): wait_time between(2, 8) def on_start(self): # 登录并提取 token resp self.client.post(/api/login, json{ username: test_user, password: 123456 }) token resp.json()[data][token] self.client.headers.update({Authorization: fBearer {token}}) task(6) def view_product_list(self): category_id random.randint(1, 20) self.client.get(f/api/products?category_id{category_id}) task(3) def view_product_detail(self): product_id random.randint(1000, 2000) self.client.get(f/api/products/{product_id}) task(1) def add_to_cart(self): product_id random.randint(1000, 2000) self.client.post(/api/cart, json{ product_id: product_id, quantity: 1 })on_start方法是每个用户启动时会执行的初始化逻辑通常用来做登录、拿 Token、初始化状态。这一点特别重要因为在真实场景中用户一定是带身份去请求的如果你不模拟登录态压测结果会失真很多。任务权重写在task(6)的参数里6、3、1 意味着这三个任务被执行的概率大约是 60%、30%、10%我们用这种方式去模拟用户在功能模块之间的流向。2.3 为什么协程架构能撑起 10 万并发直观感受一下协程相比线程的优势。一台 8 核 16G 的云主机用 Java 的线程模型去模拟 1 万个并发用户每个线程 1MB 栈光线程内存就需要 10GB这还没算线程切换的开销。而用 gevent 协程每个 greenlet 的内存开销可能只有几 KB 到几十 KB1 万个虚拟用户占用的内存可能只有几百 MB。这也是为什么 Locust 经常被拿来和 k6、Gatling 等工具做对比但它的单机在线用户数表现依然很强。但这里我要澄清一个常见的误解。Locust 的“用户数”并不等于“每秒请求数”。用户在思考时间和等待响应期间是挂起的并不占用 CPU。所以模拟 10 万用户实际打到服务器上的请求速率取决于每个用户执行任务的频率。如果平均每个用户每秒发 0.2 个请求10 万用户就是每秒 2 万个请求。如果每个用户每秒发 5 个请求10 万用户就是每秒 50 万个请求这个量级靠单台压测机基本不可能打满所以到了这个级别就需要横向扩容了。3. AI 智能体怎么和 Locust 结合起来3.1 智能体在压测体系里的定位现在大家聊 AI 智能体聊得最多的是 Agent 能自己写代码、自己调工具、自己规划任务。但我实际用下来AI 智能体在压测里最实用的价值有三块。第一块是自动生成脚本你告诉它“模拟一个用户先登录再浏览首页然后随机看三篇文章最后发表评论”它能立刻生成一份完整的 Locust 脚本。第二块是自动分析压测结果压测跑完之后会产生大量指标和采样数据智能体可以帮你定位瓶颈。第三块是自适应调整压测参数相当于让压测过程有了闭环反馈。我重点说第一块和第三块因为这两块和 Locust 的结合最紧密也是我认为“吊打 ab 测试”的核心含义——ab 只能告诉你“服务器扛不住”但它没法告诉你“用户行为哪里不合理”而 AI 智能体能做的事远不止报数。3.2 自然语言生成压测脚本的落地路径我现在的做法是先构建一套基础行为库把常用的业务操作封装成函数或模板。比如登录、查询列表、查看详情、下单、支付、上传文件、轮询状态这些是绝大多数业务系统里都会出现的原子操作。每个操作对应一段标准 Locust 代码。然后我用大模型接一个场景解析模块你输入一段自然语言描述模型负责把描述拆解成有序任务链再从行为库中检索对应的代码片段最后组装成一份可运行的 Locust 脚本。这套流程跑起来之后效果很好。以前写一个包含登录态和业务链路的压测脚本至少要花半小时到一小时现在给智能体一段业务描述几十秒就出初稿。而且由于底层是行为库里的模板生成出来的代码质量很稳定不会出现格式乱七八糟、连 requests 头都不会设置的问题。如果模型生成的代码有语法错误我会把错误信息反馈回去让它重新修最多两轮就能跑通。有人可能会说这不就是 ChatGPT 帮我写代码吗其实不完全一样。纯靠聊天式的大模型它不知道你的接口结构也不知道你的测试环境更不清楚你的性能目标。而我在智能体前面加了一层“业务场景建模”让它先理解系统上下文再输出内容质量完全不一样。3.3 智能体自动调整并发策略这块是我最新实验的功能。Locust 提供了一些事件钩子event hooks比如test_start、test_stop、request_success、request_failure。我写了一个监控模块在request_failure或request_success的回调里持续记录接口错误率和响应时间。当错误率超过阈值时智能体会判断当前是否已经触达系统瓶颈如果是它会执行一个“步降策略”——按比例减少部分任务权重或调低并发目标。说起来有点像控制论里的负反馈。智能体的具体策略是每 10 秒采集一次聚合指标如果 P95 响应时间连续三个周期都超过预设阈值就把当前负载标记为“超出承载能力”然后触发压降动作。压降不是直接停压测而是把一些高频、重资源的任务权重调低让系统先缓过来。这样跑出来的压测不再是一锤子买卖而是能自动找到系统的“最佳负载区间”。这个思路我很推荐做性能测试的朋友试试比自己盯数据人肉干预高效太多。3.4 智能体数据集与行为模型训练问题聊到 AI 智能体还有一个很实际的话题数据从哪里来。想训练一个真正理解你业务的压测智能体你需要积累“行为数据集”。例如接口的 OpenAPI 文档、线上的访问日志、用户点击流数据、历史压测脚本、故障复盘文档。把这些数据喂给大模型做微调或 RAG 向量化检索智能体的行为建模能力才会越来越准。我目前的做法是把接口文档转成 JSON 结构存进向量数据库智能体在生成脚本前先做一次语义检索把最相关的接口信息拿出来作为上下文。这套 RAG 架构让智能体的脚本生成不再依赖“猜”而是基于真实接口定义去写。这个方案对团队来说门槛不高关键是前期的数据清洗和接口文档规范化这个基础打得越扎实智能体后期表现越好。4. 10 万并发的分布式压测实战4.1 单机压测的极限在哪里说完了智能体回到硬核的压测本身。先算一笔账如果单台压测机的网络带宽是 1Gbps实际可用带宽按 800Mbps 算每个请求的平均响应体大小是 5KB那么单台压测机每秒最多能打出的请求量大约是 800 / 8 / 5 20 万请求/秒的量级。理论上看起来很多但别忘了每个请求可能还有请求体、响应头、TCP 握手、TLS 握手这些开销。如果接口响应体大一些比如 50KB那单台 1Gbps 的机器每秒只能打出 2 万请求这时候你再怎么加用户数也突破不了网卡瓶颈。所以做 10 万并发压测第一步不是纠结工具而是要先算清楚你需要多少台压力机。我的经验是单台 8C16G 的压测机在请求体小、逻辑简单的场景下跑 5 万虚拟用户问题不大但如果每个用户每秒触发多个请求负载会指数上升。稳妥的方案是先用单机测一个基准看看单机性能再按目标用户数除以单机能力的 70% 来估算需要的机器数量。4.2 分布式架构Master 和 Worker 的配合逻辑Locust 的分布式模式非常成熟。一台机器作为 Master负责协调和汇总统计其他机器作为 Worker负责实际产生压测流量。用户可以通过--master和--worker参数来启动不同角色。启动 Master 的命令locust -f locustfile.py --master --expect-workers5启动 Worker 的命令locust -f locustfile.py --worker --master-host192.168.1.100注意--expect-workers参数它告诉 Master 预期有多少个 Worker 会接入。当所有 Worker 都连上来之后Master 才会允许你点击“开始压测”。这个机制很贴心避免了测试开始后发现 Worker 没接全导致负载不均匀的尴尬。在分布式模式下你只需要在 Master 的 Web 界面里设置总的用户数和启动速率Master 会把用户数自动均匀分配给所有 Worker。比如你设置了 10 万用户5 个 Worker每个 Worker 就负责跑 2 万用户。所有 Worker 的压测结果会实时汇总到 Master 端Web 界面上能看到完整的在线用户数、请求速率、响应时间分布、异常率等指标。4.3 写一个支持 10 万并发任务的 Locust 脚本我实际项目中使用的脚本结构大概是这样:from locust import HttpUser, task, between, events import random, time class MixedUser(HttpUser): wait_time between(0.5, 3) def on_start(self): payload {username: fuser_{random.randint(1, 100000)}, password: pass} resp self.client.post(/api/v1/auth/login, jsonpayload, name登录) if resp.status_code 200: token resp.json().get(data, {}).get(token, ) self.client.headers.update({Authorization: fBearer {token}}) task(5) def get_user_profile(self): self.client.get(/api/v1/user/profile, name获取用户信息) task(3) def get_feed_list(self): self.client.get(/api/v1/feed?page1size20, name获取信息流) task(2) def submit_feedback(self): self.client.post(/api/v1/feedback, json{content: hello}, name提交反馈)注意几个设计点。第一name参数能在报表里给不同请求起别名避免 URL 里带随机参数导致同一条接口被拆成成千上万条统计记录。第二on_start里模拟了大量不同用户的登录避免所有用户共用一个账号造成服务端缓存逻辑失真。第三用random生成动态数据让请求参数更接近真实用户。4.4 压测过程中的观测指标怎么看在压测过程中我建议你盯着几个关键指标看而不是只看每秒请求数。第一个是响应时间的百分位分布特别是 P95 和 P99。第二个是异常率Locust 的界面里会有失败请求数占比这个数字如果超过 1%基本就要停下来看日志了。第三个是当前在线用户数和请求速率之间的比值如果用户数一直涨但请求速率上不去说明用户大部分时间都在等待响应这时候服务器大概率已经出现瓶颈。还有一点非常容易被忽略压测机本身的资源状况。如果你发现 Master 的 Web 界面里 RPS 上不去但服务器 CPU 并没有打满先别急着怀疑服务器不行看看压测机自己的 CPU、内存、网络连接数是不是已经爆了。我在生产环境踩过一次坑压测机是台老机器网卡中断处理能力不足跑到 3 万用户的时候网卡软中断直接占满一个 CPU 核心导致客户端发送能力受限最后表现出来的现象是“加用户但请求数不涨”后来换成性能更好的压测机才算解决。5. 常见问题与排查技巧实录5.1 并发用户数上不去一直在 pending如果你发现设置了很高的用户数但实际并发数一直起不来首先检查是不是 Target 服务的 keep-alive 连接数限制。每个虚拟用户和服务器之间有一个 HTTP 连接如果服务器端配置了过低的MaxKeepAliveRequests或连接超时时间太短连接会被频繁回收用户就会处于“假在线”状态。建议压测前先通过ss -s查看系统当前 TCP 连接状态如果大量连接处于TIME_WAIT说明连接被频繁创建销毁这时候应该调大 keep-alive 超时时间。另一个常见原因是本机文件描述符限制。Linux 默认的ulimit -n是 1024也就是说单进程最多打开 1024 个文件描述符。模拟 10 万并发用户需要至少 10 万个 socket 连接必须把压测机的文件描述符上限调大。我一般在压测前执行ulimit -n 655350 echo fs.file-max 655350 /etc/sysctl.conf sysctl -p5.2 响应时间虚高到底是谁的问题压测过程中经常遇到“服务端 CPU 只有 30%但响应时间已经到好几秒”的情况。这时候先别急着下结论说服务端不行检查三个地方第一压测机自身网络带宽是否被打满第二压测机和目标服务之间是否有负载均衡、防火墙、WAF 等中间设备这些设备是否会成为瓶颈第三目标服务的 GC 日志里 Full GC 是否频繁。我遇到过一次典型问题压测机千兆网卡目标服务在另一个机房两个机房之间的专线带宽只有 200Mbps。跑压测的时候请求量一上来专线带宽就被打满响应时间直接飙到 3 秒。排查了半天最后用iftop看流量才发现是跨机房带宽问题。这个教训告诉我压测前要先把网络链路捋清楚尤其是跨机房、跨云、过负载均衡这种复杂链路网络往往是最隐蔽的瓶颈。5.3 分布式压测的时间不同步问题在 Master-Worker 分布式模式下如果各 Worker 机器的系统时间不一致压测结果的汇总看起来会有偏差。比如某些 Worker 上请求耗时明显偏高但实际上只是该机器时钟慢了。我的做法是在每台 Worker 上配置 NTP 服务同步时间确保分布式环境时间一致。另外一条更实用的经验把所有 Worker 放在同一个内网环境避免跨公网带来的额外时延和不稳定性。公网环境下 Worker 到目标服务的网络波动会把真实的服务性能指标淹没掉你根本分不清延迟高是服务慢还是网络慢。5.4 AI 智能体生成的脚本跑不通怎么办我在使用智能体生成的脚本时也遇到过不少问题最常见的是它生成的接口路径和真实接口不一致或参数格式错误。我的排查套路是先让它在本地单机模式跑 1 个用户 10 个请求打开--headless模式关掉 Web 界面直接看控制台报错信息。如果报错信息不够直观我会把智能体的上下文里加上真实的接口抓包数据让它重新生成。现在我把这套流程固化成了一个循环业务描述 → 智能体生成脚本 → 单机冒烟测试 → 错误信息反馈 → 智能体修复 → 小规模验证 → 规模化执行。这个循环跑熟了之后一套压测场景从描述到可执行时间基本控制在 15 分钟以内。6. 支撑 10 万并发的脚本设计要点与思考6.1 脚本里的用户状态管理在压测脚本设计中最容易踩坑的是用户状态管理。很多人写 Locust 脚本时把所有逻辑写在task方法里但用户登录状态、购物车数据、浏览历史这些会话状态全都丢掉了。这样压出来的结果只代表“游客流量”根本不代表真实业务。我在脚本里一般用两个层面的状态管理。第一层是用户级状态也就是on_start里初始化的状态。比如登录后拿到的 Token、用户 ID、个人信息这些放到self属性里即可。第二层是任务级状态比如先创建订单再支付再查询物流这三步之间有数据依赖需要把上一步的响应结果通过局部变量传给下一步。这个操作对 Locust 来说完全没难度加一个类属性或者局部变量就行。但这里有个性能隐患如果on_start里逻辑太重比如每次用户启动都要执行一次复杂登录、初始化很多数据那么大规模压测启动时所有协程会同时挤在on_start阶段造成请求尖峰。我的解决办法是给on_start里加随机延迟或者把初始化数据拆到多步错峰执行。6.2 模拟真实用户行为而不是模拟真实压力这是我这几周最深的一个体会。很多人理解压测以为压测就是不断增加压力把服务打挂为止。但从业务角度出发真正有价值的压测是“用户行为模拟”是要回答在真实用户行为模式下系统能不能扛得住。同样是每秒 5000 个请求可能是 5000 个用户无脑刷一个静态页面也可能是 500 个用户完成一个七步下单流程。前者充其量测了 Web 服务器的静态文件处理能力后者才能测出整个业务链路的真实瓶颈。AI 智能体的加入本质上就是让“用户行为模拟”这个环节更容易。以前我们要手动写一堆脚本去模拟用户动线现在可以通过自然语言描述直接生成。我甚至设想过一种状态让智能体基于生产环境的访问日志自动挖掘用户的热门行为路径然后生成一套压测脚本。这样压测场景不再是测试人员拍脑袋想出来的而是来自线上真实数据这比任何手写脚本都更有说服力。6.3 把单台压测机的资源用到极致如果你不想一开始就铺很多台压测机可以先把单机的性能优化到极限。我这里有几个实用的优化项。第一把statsd或influxdb这类指标上报关掉避免不必要的网络开销。第二调大系统网络缓冲区参数net.core.rmem_max 134217728 net.core.wmem_max 134217728 net.ipv4.tcp_rmem 4096 87380 134217728 net.ipv4.tcp_wmem 4096 65536 134217728 net.ipv4.tcp_timestamps 0这样可以提高 TCP 吞吐量。第三如果目标接口是 HTTPS尽量在压测机上开启 SSL 会话复用减少每次请求时的 TLS 握手开销。Locust 底层使用了 requests 库requests 底层是 urllib3我通过在HttpUser里传入connection_pool_maxsize和pool_maxsize参数把连接池调大复用已有连接。6.4 一键启动 10 万并发的完整流程最后把整个流程串一遍。假设你已经有了一台 Master 和五台 Worker并且已经把压测脚本放到了所有机器上那么完整的执行过程是这样的在所有机器上安装依赖并检查环境pip install locust确认 Python 版本和系统参数。启动 Masterlocust -f locustfile.py --master --expect-workers5 --web-port8089。启动五台 Worker每台执行locust -f locustfile.py --worker --master-host192.168.1.100。打开 Master 的 Web 界面设置总用户数为 100000设置启动速率为每秒 1000 个用户。观察 Prometheus 或 Grafana 面板持续关注服务端的 CPU、内存、GC、网络以及中间件指标。跑完测试后在 Master 端导出 CSV 报告或者直接把时序数据导入 InfluxDB 用 Grafana 展示。这个流程我实际跑过很多次稳定性很高。唯一要提醒的是10 万并发用户跑起来流量是真实存在的如果没有提前和运维沟通、做好资源隔离可能会影响同机房的其他业务。我在生产压测前一定会先发变更单、拉群通知、评估风险这是压测的基本职业素养。从 ab 到 Locust从手工脚本到 AI 智能体辅助生成压测这个领域确实在慢慢“变天”。但变化的不是工具本身而是我们对压测的理解压测不再是简单粗暴地制造高负载而是要更聪明、更贴近真实用户地去模拟业务行为。工具只是实现目标的手段懂业务、懂系统、懂数据才是压测工程师真正的核心竞争力。我个人的建议是大家动手试试 Locust 的分布式能力然后把 AI 智能体当作“脚本生成加速器”用起来你会发现压测这件事的效率和深度都会上一个大台阶。最后再分享一个小技巧压测脚本里一定要记得加--stop-timeout参数否则压测结束后 Worker 可能还在跑残余任务白白浪费资源。这些细节都是踩过坑之后才会注意到的。