微服务架构下基于Locust构建高仿真全链路压测体系
2026/9/8 6:37:12 网站建设 项目流程

做压测这几年,我最大的感受是:大部分团队不是不会用工具,而是根本不知道自己要压什么。尤其是微服务架构铺开之后,系统链路动辄跨越五六个服务、十几个中间件,你还拿十年前那套“对单个接口狂点2000次”的思路去做压力测试,压出来的数据基本属于自欺欺人。今天这篇内容,我会结合我在多个分布式项目里落地 Locust 的完整过程,拆解一套“高仿真压力测试体系”到底该怎么设计、怎么实现、怎么把结果用起来。全文偏实战,代码和配置都能直接抄,适合正在搭压测平台的后端开发、测试架构师,以及被线上事故逼着做全链路压测的朋友。

1. 微服务架构下,压测这件事为什么变难了

1.1 从单机压测到全链路压测的转变

单体应用时代,压测的核心逻辑很简单:起一个压测工具,对着接口猛打,看 Tomcat 线程池是不是被打满、数据库连接池是不是被打爆。问题定位通常两三步就到了根因,因为链路短、依赖少、故障边界清晰。

微服务架构把这个逻辑彻底打破了。一个用户下单请求,可能要经过网关、鉴权服务、订单服务、库存服务、支付服务、消息队列、缓存、数据库,甚至还要回调第三方接口。任何一个环节出现瓶颈,都会让整个链路的表现变得非常诡异——可能是超时、可能是重试风暴、也可能是服务雪崩。

在这个背景下,“压力测试”这件事的目标就不只是“测出系统能扛多少 QPS”,而是要回答几个更尖锐的问题:

  • 当某个基础服务出现性能劣化时,整个链路会受到多大影响?
  • 突发流量下,限流阈值设多少才既能保护系统、又不会误伤正常用户?
  • 扩容多少个实例,才能让核心接口的 P99 稳定在目标值以内?
  • 缓存击穿、消息积压这种极端场景下,系统靠熔断和降级能不能兜住?

这些问题的答案,靠“打打单接口”是永远得不到的。你需要一套能够模拟真实业务用户行为、能够构建复杂业务链路、能够在分布式环境下大规模施压的压测体系。

1.2 传统压测工具的局限性

我最早是用 JMeter 做压测的,说实话,JMeter 在 GUI 脚本录制、断言配置、插件生态方面确实成熟,但它有几个在现代微服务架构下越来越难忍的问题。

第一,内存和线程模型问题。JMeter 每个虚拟用户对应一个线程,线程数一多,JVM 就要吃大量内存,单机模拟几千用户基本就到顶了。第二,脚本维护成本高。在微服务场景里,压测脚本要处理动态 token、时间戳、签名、参数关联,这些逻辑在 JMeter 的配置元件里写起来非常繁琐,稍复杂点就要写 BeanShell 或 JSR223 脚本,调试体验一言难尽。第三,分布式协调能力弱。JMeter 虽然有 master-slave 模式,但脚本分发、结果汇聚、资源调度都不够灵活,大规模压测时经常出现 worker 负载不均、数据采集延迟的问题。

相比之下,Locust 从设计思路上就更贴合现代服务端的压测需求。它是基于 Python 的,通过事件驱动和协程模型实现高并发,单进程就能模拟数千用户,而且压测场景本身就是普通的 Python 代码——这意味着你可以用 py 脚本处理任何协议、任何鉴权逻辑、任何数据关联,和 CI/CD 的集成也天然顺滑。更重要的是,Locust 的每个虚拟用户就是一个协程,它的行为完全由代码定义,你能模拟出“用户看了一会儿商品、先加购再结算”这样的真实行为序列,而不是简单粗暴地砸请求。

2. 高仿真压测体系的设计思路拆解

2.1 从“压接口”升级到“压场景”

很多人设计压测方案,一上来就说“我要压 /api/login 这个接口,500 并发”。这个思路的问题在于:真实用户根本不会只打一个接口。

真实用户在 App 里的操作序列是:打开首页 -> 浏览商品列表 -> 点进详情 -> 加入购物车 -> 结算 -> 支付。如果我们只对其中一个接口做压测,得到的 TPS 和响应时间完全没有参考价值,因为系统是在“无业务上下文”的状态下被压的,各种缓存命中率、连接池状态、服务间调用关系都和真实业务跑的时候完全不同。

所以我的核心思路是:压测的单元不是接口,而是业务场景。每个压测进程模拟一类真实用户,每个用户按照一定的概率分布去执行业务操作序列,序列中的每一步对应一次或多次网络请求。

比如在一个电商系统里,我会设计这么几种虚拟用户:

  • 游客用户:只浏览,占 30%
  • 登录用户:浏览 + 加购,占 40%
  • 深度购买用户:浏览 + 加购 + 下单 + 支付,占 20%
  • 后台运营用户:查询订单 + 导出报表,占 10%

每种用户设置不同的权重和操作频率,压测压力就会分布在多个服务和 API 上,整个系统的瓶颈才有可能暴露出来。这才是真正意义上的“高仿真”。

2.2 模拟真实用户行为的三大核心要素

想让人工脚本逼近真实用户,至少得把握好三个核心要素:思考时间、行为权重、数据特征

思考时间(Think Time)用什么表示?真实用户在页面上浏览商品的时间可能是 3 秒,提交订单前犹豫的时间可能是 10 秒。在 Locust 里,这就对应wait_time参数。压测新手最容易犯的错误就是不设置思考时间或者把思考时间设成固定值,结果所有虚拟用户像机器人一样步调一致地发请求,这对系统产生的压力和真实场景完全两回事。

更合理的做法是让思考时间在一个范围内随机分布,比如between(1, 5),甚至用constant_pacing来控制每秒最多发多少个请求,这样模拟出来的流量曲线会更平滑、更接近真实。

行为权重对应的就是任务权重(@task装饰器里的权重数值)。真实用户访问系统的频次是有统计规律的,比如“点击查看商品”的频率远高于“支付”。我们根据埋点数据或业务经验统计出真实比例,再折算成 Locust 的 task weight,这样压测流量里的业务结构才是对的。

数据特征是最容易被忽略但影响最大的点。真实用户请求里带的用户 ID 是分散的、商品 ID 是多样的、搜索关键词是长尾的。如果你在脚本里硬编码了同一个用户 ID 和同一个商品 ID,那么压测时长一长,redis 缓存全是同一个 key,数据库请求集中在同一条记录上,压出来的结果不仅分毫不准,还可能直接把数据打脏。

所以我会在脚本里做大量的数据随机化:从预置的用户池中随机取数、从商品表里随机挑选、按规则动态生成手机号和身份证号,确保压力均匀分布在整个数据集合上。

2.3 为什么选择 Locust 而非自研压测平台

这里多说一句选型的事。有的团队条件比较好,会想着基于 Netty 或者 Go 协程自研一套压测平台,这听着很酷,但其实成本非常高。压测平台最难的部分从来都不是“发送请求”,而是“流量模型的管理”“执行过程的观测”“结果的统计分析”,这三块做好任何一个都需要持续投入。我记得很清楚,有一次和一个做电商中台的架构师聊天,他的团队花了两三个月自研压测器,最后发现他们在做的功能 Locust 全都有,而且社区维护得更好。

Locust 的价值在于它把“高并发模拟用户”这个最难的部分做透了,同时保留极高的开放性。扩展协议、接入监控、定制结果处理都通过 Python 代码解决,学习曲线极低——任何一个会点 Python 的测试开发都能在半天内上手。所以我的经验是:不是巨型平台强烈建议站在 Locust 的肩膀上,把精力省下来投入到场景设计和结果分析里去,产出会大得多。

3. Locust 核心机制与高仿真脚本编写实战

3.1 搭建基本的测试工程

先交代一下环境。我习惯把压测代码做成一个独立的 Python 工程,用requirements.txt管理依赖,这样不管是本地调试还是放到压测机上部署,都不会出现环境不一致的破事。

pip install locust

安装好之后,目录结构建议这样组织:

loadtest/ ├── locustfile.py # 入口脚本 ├── locustfiles/ │ ├── buyer_user.py # 买家用户行为 │ ├── seller_user.py # 商家用户行为 │ └── admin_user.py # 运营用户行为 ├── data/ │ ├── user_pool.csv # 用户数据池 │ └── product_pool.csv # 商品数据池 ├── utils/ │ ├── auth.py # 签名与token生成逻辑 │ └── data_mock.py # 数据随机化工具 └── conf/ └── settings.py # 压测环境配置

一个最小的locustfile.py长这样:

from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time = between(1, 3) @task def index_page(self): self.client.get("/")

这段代码里,WebsiteUser模拟一个真实用户,每个用户每隔 1 到 3 秒随机执行一次index_page请求。当你启动 Locust 后,它会在每个协程里循环执行这个用户类的行为,这就替代了 JMeter 里“线程组 + 采样器”的组合。

3.2 高仿真用户脚本的三层结构

在真实项目里,我的用户脚本从来不直接写成一个扁平的函数列表,而是按照“用户角色 -> 业务阶段 -> API调用”三层结构来组织。这里以电商系统为例,给你看一段核心代码。

import random from locust import HttpUser, task, between from utils.auth import AuthManager from utils.data_mock import MockData from conf.settings import USER_TOKEN_FILE, PRODUCT_POOL class BuyerBehavior: """买家用户的行为集合,被 HttpUser 任务调用""" @staticmethod def browse_home(user): user.client.get("/api/v1/home/recommend", headers=user.auth_headers) @staticmethod def search_product(user): keyword = random.choice(["手机", "蓝牙耳机", "卫衣", "跑步鞋"]) user.client.get( "/api/v1/search", params={"keyword": keyword, "page": 1, "page_size": 20}, headers=user.auth_headers, ) @staticmethod def view_product_detail(user): product_id = random.choice(PRODUCT_POOL) user.client.get( f"/api/v1/product/{product_id}", headers=user.auth_headers, ) @staticmethod def add_cart(user): product_id = random.choice(PRODUCT_POOL) user.client.post( "/api/v1/cart/add", json={"product_id": product_id, "quantity": 1}, headers=user.auth_headers, ) @staticmethod def checkout_and_pay(user): # 先获取购物车信息 resp = user.client.get("/api/v1/cart/list", headers=user.auth_headers) if not resp.ok: return cart_items = resp.json().get("data", {}).get("items", []) if not cart_items: return # 创建订单 order_resp = user.client.post( "/api/v1/order/create", json={ "items": [ {"sku_id": item["sku_id"], "quantity": item["quantity"]} for item in cart_items[:2] ], "address_id": user.address_id, }, headers=user.auth_headers, ) if order_resp.ok: order_id = order_resp.json().get("data", {}).get("order_id") user.client.post( f"/api/v1/order/{order_id}/pay", json={"pay_type": "mock_balance"}, headers=user.auth_headers, ) class BuyerUser(HttpUser): wait_time = between(1, 4) def on_start(self): """虚拟用户启动时执行:登录并准备公共数据""" token = AuthManager.get_random_token() self.auth_headers = {"Authorization": f"Bearer {token}"} self.address_id = MockData.random_address_id() @task(30) def do_browse(self): """30% 的浏览行为""" action = random.choice([BuyerBehavior.browse_home, BuyerBehavior.search_product]) action(self) @task(20) def do_view_and_cart(self): """20% 的浏览+加购行为""" BuyerBehavior.view_product_detail(self) if random.random() < 0.7: BuyerBehavior.add_cart(self) @task(10) def do_full_purchase(self): """10% 的完整购买行为""" BuyerBehavior.view_product_detail(self) BuyerBehavior.add_cart(self) BuyerBehavior.checkout_and_pay(self)

这段代码的重点在于:on_start方法会在虚拟用户启动时先完成登录和初始化,这和真实用户“打开 App 先登录再看内容”的行为一致;@task后面的权重数字决定了某种行为被执行的概率;每个任务里通过随机函数改变访问的商品、关键词,避免数据倾斜。

这里有一个非常容易踩的坑,必须单独拎出来说:on_start里的操作要尽量轻量,不要在这里做大量数据库轮询或循环操作。因为每个虚拟用户启动时都会执行一次,如果on_start本身耗时超过 1 秒,5000 个用户光是初始化就要花掉十几分钟,而且这些初始化请求也会打到被测系统上,污染压测数据。我在实战里通常会让on_start只做“登录 + 缓存必要的用户数据”,其余后置准备动作放到第一次任务的if not hasattr(self, 'initialized')判断中去执行。

3.3 实现阶梯加压模拟真实流量洪峰

真实线上流量的增长从来不是一蹴而就的。秒杀场景、大促场景、消息推送后的集中访问,流量曲线都是先平缓、再陡升、再回落。如果压测一上来就直接打满压力,被测系统可能会因为“没有预热”而提前触发限流或崩溃,导致你根本测不出它的真实容量。

Locust 的--step-load参数可以解决这个问题。它会按照你设定的步长逐步增加用户数,让系统在每一档压力下运行一段时间,然后再进入下一档。用命令行也能直接实现:

locust -f locustfiles/buyer_user.py \ --host=https://api.example.com \ --headless \ -u 1000 \ -r 100 \ --run-time 30m \ --step-load \ --step-users 100 \ --step-time 3m

这条命令的含义是:从 100 个用户开始,每 3 分钟增加 100 个用户,直到总用户数到 1000,全程跑 30 分钟。这种阶梯加压的好处是你可以非常清晰地观察到系统在哪个并发量级上开始出现响应时间拐点、错误率跳变,进而推算出服务的“软极限”。

如果你有更复杂的加压策略,比如先快速冲到 80% 的预估峰值维持 5 分钟,再缓降到 20% 验证恢复能力,我建议直接在代码里使用LoadTestShape实现自定义流量模型:

from locust import LoadTestShape class StepLoadShape(LoadTestShape): """ 自定义流量模型:在指定时间点设置目标并发数 """ time_limit = 1800 spawn_rate = 20 def tick(self): run_time = self.get_run_time() if run_time < 300: return (200, spawn_rate) elif run_time < 600: return (600, spawn_rate) elif run_time < 1200: return (1000, spawn_rate) elif run_time < 1500: return (400, spawn_rate) elif run_time < 1800: return (800, spawn_rate) else: return None

这种写法把流量模型变成了代码,方便版本化管理,也方便压测后评估“当前这种流量曲线下系统的表现是否满足容量目标”。

4. 分布式压测与监控观测体系建设

4.1 大规模压测下的分布式部署

Locust 的分布式模式不算复杂,但部署方式有一些设计取舍。核心架构是 master 节点负责调度和结果汇聚,多个 worker 节点负责实际的压力产生,web 界面在 master 节点上启动,浏览器里可以直接实时观测各节点的状态。

启动命令是:

# master locust -f locustfiles/buyer_user.py --master --host=https://api.example.com --web-port 8089 # worker,可以启动多个 locust -f locustfiles/buyer_user.py --worker --master-host=192.168.1.10 --master-port=5557

这里有几个实战心得想要分享。

第一,worker 节点的机器性能决定了单机能压多少压力。虽然 Locust 是协程模型,单机几千用户很轻松,但如果你的虚拟用户里有复杂的计算逻辑(比如签名、加解密、业务断言),CPU 就容易成为瓶颈。所以我习惯在 worker 节点上先通过-u 500看当前机器的 CPU 使用率,再决定往上加。

第二,worker 之间是无状态的,每个 worker 的run_single参数不用设置,所有状态同步由 master 负责。但要注意,当你使用分布式模式时,每个虚拟用户启动时执行on_start并发量是乘上 worker 数量的。比如 10 个 worker、每个 worker 分配 500 个用户,那就是 5000 个用户同时执行on_start登录,对登录接口的冲击相当大。所以做分布式大压测前,我会先单独评估登录校验服务能扛多少并发,必要时给on_start加一个随机延迟或者把用户初始化预置到内存缓存里。

第三,master 节点千万不要参与施压。master 的职责是汇总所有 worker 的采样数据、维护 web 界面,它本身也会占用不少 CPU 和内存,如果再让它跑压测任务,整个集群的数据收集都可能延迟。

4.2 压测执行过程中的监控观测清单

压测跑起来之后,不能只盯着 Locust 网页上的 TPS 曲线看,那可观测性太弱了。系统到底是在哪一个环节开始变慢,需要靠外部监控来回答。

我会在压测前,确认以下几个指标源都能正常输出:

  • 应用层 JVM 指标:堆内存、GC 频率和时长、线程池活跃度
  • 数据库指标:连接数、活跃会话数、慢查询数量、锁等待
  • 中间件指标:Redis 命中率、消息队列积压量、缓存穿透情况
  • 基础设施指标:CPU、内存、磁盘 IO、网络带宽

如果团队已经接入了 Prometheus + Grafana,可以直接在压测时打开对应的 Dashboard 做实时观察。没有这套体系的团队,我强烈建议至少用topdstatss -s这几个命令在压测机上盯一眼资源消耗,否则你很难判断压测瓶颈是被测系统本身,还是施压机器自己先扛不住了。

这里额外提一个细节,也是我踩过很多次坑才总结出来的:压测过程中要记录“时间戳同步”的问题。Locust 侧的数据是压测机的本地时间,Grafana 上的监控数据是服务器本地时间,如果两边时间偏差超过 1 分钟,你对着 TPS 曲线和 CPU 曲线找关联时会发现对不上。所以压测前,第一件事就是确认所有压测机、被测服务器、监控采集器的 NTP 时间同步正常。

4.3 高仿真体系中的全链路标记

在微服务架构下,单纯的“接口 TPS 下降”只能告诉你系统慢了,不能告诉你是哪个服务慢了。要定位到具体节点,必须依赖全链路追踪(Trace)。

我在压测脚本里会在请求 Header 中主动注入trace_idsence_tag两个字段。这样压测产生的每一个请求,都会在链路追踪平台中形成一个完整的调用链,你可以在 trace 里按sence_tag过滤出所有压测流量,然后按服务维度统计耗时分布,一眼看出瓶颈在哪个服务。

import uuid headers = { "Authorization": f"Bearer {token}", "X-Trace-Id": str(uuid.uuid4()), "X-Scene-Tag": "fd2024_stress_test", }

这个做法在平时的普通压测里价值可能不明显,但在全链路压测时价值极大。因为你压测产生的流量和真实的业务流量是混在一起的,如果没有标记,你在排查问题时很容易误伤线上真实请求。用sence_tag可以把压测流量“染色”,观测平台里可以单独筛选、单独分析,也不会污染线上监控告警。

5. 实战案例:一次订单服务链路的全流程压测

5.1 场景设计与压测目标评估

下面我用一个订单服务链路完整走一遍压测流程,方便你对照落地。

业务背景:一个电商平台的核心下单链路,经过网关 -> 订单服务 -> 库存服务 -> 用户积分服务 -> 消息队列。测试目标是大促前评估系统容量,核心指标定为:

  • 系统整体 TPS 达到 3000
  • 订单接口的 P95 响应时间不超过 600ms
  • 错误率低于 0.1%
  • 压测过程中不允许出现雪崩(即一个服务故障导致链路全部挂掉)

在场景设计阶段,我把压测流量分成了两类:第一类是稳定流量,模拟日常用户的下单行为,占 70%;第二类是突发流量,模拟大促秒杀带来的瞬时峰值,占 30%。稳定流量用BuyerUser脚本跑,突发流量单独写一个FlashSaleUser,行为是直接访问秒杀接口。

5.2 压测执行过程与关键数据记录

先跑一次小规模基线测试,使用 200 用户执行 10 分钟,目的是确认脚本本身没有 bug,同时获取基础性能数据。这一步很重要,很多团队直接把压测跑出问题来才发现脚本写错了,浪费一整天。

基线测试后,我用前面提到的LoadTestShape做阶梯加压,按 200 -> 500 -> 1000 -> 1500 -> 2000 五档推进,每档维持 5 分钟。在每档切换的间隙,我记录了当时的 TPS、P95 响应时间和错误率。

这里给你展示一段我实际记录的数据(已脱敏):

并发用户数TPSP95耗时(ms)错误率订单服务CPU%
2006203000.01%30%
50015004200.02%55%
100028505800.05%82%
1500310012002.3%95%
20001800300015%98%

从数据里能看到,系统在 1000 并发时表现还在可接受范围内,但到 1500 并发时,P95 直接飙到 1200ms,错误率开始抬头,整体 TPS 不升反降——这是一个非常典型的系统容量拐点信号。我继续往上升用户数,TPS 反而掉到了 1800,说明系统已经进入了过载区域。

5.3 瓶颈定位与容量拐点分析

看到压力上不去、错误率升高,第一反应不是急着加服务器,而是去定位瓶颈。我用全链路追踪平台把 1500 并发时间段的 trace 调了出来,按服务维度统计各节点耗时占比,发现库存服务的耗时从正常的 80ms 暴涨到 900ms。

再点进库存服务的 trace 详情,发现它的大部分耗时卡在数据库查询上。进数据库监控一看,压测过程中出现了大量SELECT * FROM stock WHERE sku_id = ?的慢查询,而且连接数已经跑满。结合代码分析,根因是库存服务的热点商品数据集中在某几个 sku 上,这种数据倾斜导致同一行记录被大量并发更新,行锁竞争严重。

这个瓶颈只靠增加订单服务的实例是解决不了的。最终给出的调整方案是:库存服务对热点 sku 做多级缓存 + 更新操作异步化削峰,同时把数据库连接池从 50 放大到 100,并将库存扣减改为乐观锁 + 重试机制。

优化完成后,我在同样流量模型下重新压测了一轮。对比结果:并发 1500 时,P95 从 1200ms 降到 450ms,错误率从 2.3% 降到 0.02%,系统容量从支撑 1000 并发提升到了 1800 并发。

这里有一个非常重要的复盘心得:压测的核心价值不是测出一个数值,而是找到系统从空闲到过载之间变化的“全过程”。只有你掌握了这种全过程,你才知道线上真正出问题时,系统是从哪一步开始崩的、应该优先扩容哪个服务、限流阈值该设多少。

6. 常见问题与压测避坑实录

6.1 并发用户数与 TPS 之间为什么对不上

很多刚上手 Locust 的同学会问:我设置了 1000 个并发用户,为什么 TPS 只有 200 多?这完全正常。TPS(吞吐量)取决于每个用户的请求频率,也就是wait_time和任务耗时的综合结果。如果每个用户平均每 5 秒只发一个请求,那么 1000 个用户理论上最大 TPS 也就是 1000 / 5 = 200 左右。

理解这个关系很重要:在 Locust 里,“并发用户数”是压力输入,TPS 是系统输出,二者中间隔着用户行为。如果你想提高 TPS,要么提高并发用户数,要么降低单个用户请求之间的等待时间。我见过有人追求高并发数据,直接把wait_time删掉了,结果每一个虚拟用户都在疯狂循环打请求,别说真实了,被测系统基本见不到这种流量形态。

如果需要严格控制请求速率,建议用constant_pacing

class BuyerUser(HttpUser): wait_time = constant_pacing(2)

这个参数的意思是确保每个用户每秒最多发出 1 个请求(加上执行时间,平均每 2 秒一个请求),这样 TPS 的估算会非常线性。

6.2 分布式压测时 worker 之间负载不均

跑分布式压测时经常出现 10 个 worker 里有两个 CPU 已经飙到 95%,另外几个还在 20% 徘徊。这个现象在高 TPS 场景下尤其明显。排查思路是看这几个高负载的 worker 上是否分配了大量虚拟用户——正常情况下 Locust 的分配是均匀的,但如果你启动了多个 user class,某些 worker 可能因为用户类实现逻辑不同而负载更重。

解决办法有两个维度。一是把计算密集型的部分(比如加解密、签名)从用户脚本中抽出来,做成独立的预处理服务,压测时直接调用接口,减少 worker 侧 CPU 消耗。二是根据 worker 机器的硬件配置调整用户分配,通过--run-time配合--processes参数做多进程绑定,让性能强的机器多跑一些用户。

我踩过的另一个隐蔽的坑是:worker 节点的系统文件句柄数不够。当你的虚拟用户发起大量 HTTP 连接时,Linux 的ulimit -n默认值(通常 1024)会直接卡住连接,导致压测过程出现大量ConnectionError。启动压测前务必在 worker 节点执行一下ulimit -n 65535,或者写入/etc/security/limits.conf持久化。

6.3 响应时间数据剧烈跳动,到底信谁

很多压测场景下会出现 TPS 曲线和响应时间曲线剧烈抖动的现象,有时候 P95 一下子从 200ms 跳到 3000ms,然后又恢复。遇到这种情况,我一般按顺序排查三个问题。

第一步看压测机资源。如果 worker 的 CPU 已经跑满,那么它本身的调度和网络处理就会变慢,这个慢会被测系统完全不相关。第二步看网络链路。压测机与目标机器如果不在同一个内网,公网抖动会造成响应时间漂移,影响判断。第三步才回到被测系统自身,看是否有 GC、弹性伸缩、缓存穿透。

这里分享一个我的习惯:正式压测前先做一次“空跑”——不压被测系统,只压一个开发环境临时起的 Hello World 服务,目的是验证压测环境和压测机本身的稳定性。空跑都抖动的话,就别谈测系统了。

6.4 压测数据污染线上库的惨痛教训

我见过一个真实事故:有人在测试环境做压测,脚本里写死了手机号 13800000000,结果一个下午跑了几十万次请求,把测试环境里用户表的唯一索引直接写满了。还有人因为压测造出的数据太多,把生产前的预发环境磁盘打爆了。

针对这个问题,我的经验是三个方面一起做。第一,压测前必须做好数据造数和数据清理方案。用独立的数据池,脚本里所有生成的数据都打上前缀或标记,压测结束后一键清理。第二,涉及唯一的字段,比如手机号、身份证号,用时间戳加随机数生成,从根上避免碰撞。第三,也是最重要的,压测脚本要经过 peer review 才能上压测机。代码评审不只看逻辑对不对,还要重点看有没有“会造成脏数据的操作”。

Locust 本身没有提供数据清理的工具,通常都是靠脚本外层的 Python 脚本或运维平台完成。在压测执行结束后,我会立即触发一个清理任务,防止脏数据残留在系统里。

7. 从 0 到 1 建设压测体系的经验沉淀

跑完一次全流程压测,不要急着写总结报告就完事了。压测体系之所以叫“体系”,是因为它会持续迭代、持续沉淀。我一般会建议团队把压测积累的产物固化为三类资产。

第一类是压测场景库。每测一个业务模块,就把对应的虚拟用户脚本、流量模型、压测数据整理进代码仓库,下次再想测直接复用。第二类是容量基线库。记录每次压测的环境拓扑、并发用户数、TPS、P95、资源使用率等关键数据,形成一套按版本演进的历史容量画像。第三类是监控联动配置。把压测时使用的监控大盘、告警规则模板沉淀到配置中心,保证每次压测的观测口径一致。

这三类资产的价值,平时看不出来,但到了真正需要快速决策的时刻——比如某次版本上线前要评估是否可以承受翻倍流量——你能很快给出一个不至于「拍脑袋」的评估。最后说一个我自己很深的体会:压测不是一次性的工程,它更像是给系统做定期的“体能测试”,一次跑完不意味着将来永远健康。唯一让测试结果可信的方法,就是持续把每一轮压测暴露出的问题修完、验证完,把压测变成研发流程的一部分,而不是上线的附加仪式。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询