从零掌握Locust性能测试:Python代码化压测与分布式实战

📅 2026/7/6 1:35:22 👁️ 阅读次数 📝 编程学习
从零掌握Locust性能测试:Python代码化压测与分布式实战

1. 项目概述:为什么我们需要Locust这样的性能测试工具?

在软件开发和运维的日常里,性能测试常常是一个“说起来重要,做起来麻烦”的环节。很多团队要么用着笨重的商业工具,要么自己写脚本模拟请求,前者成本高、学习曲线陡,后者难以模拟真实并发、数据统计和分析更是头疼。我自己带过不少项目,从零到一搭建过性能测试体系,深知一个趁手的工具对效率提升有多大。直到遇到Locust,我才发现用Python写性能测试脚本可以如此优雅和高效。

Locust的核心魅力在于“代码即配置”。它允许你用纯Python代码来定义用户行为,这意味你可以利用Python生态里的一切库(比如requests, aiohttp, beautifulsoup)来构造极其复杂的业务场景,而不仅仅是发个HTTP GET请求。你不需要去学习某个工具特有的DSL或图形界面,对于Python开发者来说,几乎是零成本上手。它的分布式特性可以轻松地将负载生成器扩展到多台机器,模拟数十万级别的并发用户。更重要的是,它的测试结果通过一个简洁的Web界面实时展示,RPS(每秒请求数)、响应时间、用户数等关键指标一目了然。

这篇文章,我会从一个实战者的角度,带你从零开始搭建Locust环境,编写一个贴近真实业务的测试脚本,并深入讲解如何分析结果、定位瓶颈以及搭建分布式集群。无论你是刚接触性能测试的QA工程师,还是需要为自己API服务做压测的后端开发者,甚至是运维同学,这篇教程都能给你一套可直接复用的“作战方案”。我们不止讲怎么用,更会讲为什么这么用,以及我在实际压测中踩过的那些坑。

2. 环境准备与Locust安装全攻略

工欲善其事,必先利其器。Locust的安装非常简单,但不同的安装方式对应着不同的使用场景。我会详细对比几种主流方式,帮你做出最适合自己的选择。

2.1 Python环境基石:版本管理与虚拟环境

Locust支持Python 3.7及以上版本。我强烈建议你使用虚拟环境来管理项目依赖,这能避免不同项目间的包版本冲突,是Python开发的最佳实践。

方案一:使用venv(Python原生,推荐)这是最轻量、最标准的方式。假设你的项目目录叫locust_demo

# 创建项目目录并进入 mkdir locust_demo && cd locust_demo # 创建虚拟环境,环境目录名为 .venv python3 -m venv .venv # 激活虚拟环境 # 在Windows上: .venv\Scripts\activate # 在macOS/Linux上: source .venv/bin/activate

激活后,你的命令行提示符前通常会显示(.venv),表示你已处于该虚拟环境中。之后所有pip install操作都只影响这个环境。

方案二:使用Conda(适合数据科学或已有Conda环境的同学)如果你已经安装了Anaconda或Miniconda,这也是一个不错的选择。

# 创建一个名为locust,Python版本为3.9的新环境 conda create -n locust python=3.9 # 激活环境 conda activate locust

实操心得:我个人的习惯是每个性能测试项目都单独创建一个虚拟环境。因为不同项目依赖的第三方库版本可能不同(比如有的用requests 2.25,有的用2.28),混用可能导致脚本报错。将venv目录添加到.gitignore中,然后在项目根目录放一个requirements.txt文件来记录依赖,这样团队协作和部署时环境就能保持一致。

2.2 Locust的多种安装方式与选型

有了Python环境,安装Locust就一行命令的事。但这里有几个变体需要了解。

1. 标准安装(最常用)

pip install locust

这条命令会安装Locust核心及其常用依赖。对于绝大多数HTTP/HTTPS协议的压测场景,这就足够了。

2. 安装带有UI扩展的版本(如果需要WebSocket等)Locust的核心库只支持HTTP。如果你需要测试WebSocket、gRPC等协议,或者想使用更丰富的Web UI组件,可以安装扩展版。

pip install "locust[extra]"

这个版本包含了locust-plugins等额外库,提供了更多类型的客户端和监听器。对于新手,我建议先从标准版开始,明确有需求后再升级。

3. 通过Docker运行(追求环境一致性)如果你不想污染本地环境,或者需要在CI/CD流水线中运行压测,Docker是最佳选择。

# 拉取官方镜像 docker pull locustio/locust # 运行一个最简单的示例,将当前目录挂载到容器内的 /mnt/locust docker run -p 8089:8089 -v $PWD:/mnt/locust locustio/locust -f /mnt/locust/locustfile.py --host=https://your-target-server.com

使用Docker时,你需要将你的测试脚本(locustfile.py)放在当前目录。-p 8089:8089将容器的Web UI端口映射到宿主机。这种方式隔离性最好,但调试脚本稍微麻烦一点,需要反复构建镜像或使用卷挂载。

验证安装安装完成后,在命令行输入:

locust --help

如果能正常输出帮助信息,说明安装成功。

注意事项:在Windows系统上,如果遇到与gevent(Locust使用的并发库)相关的安装错误,通常是Microsoft Visual C++ Build Tools缺失导致的。你可以从微软官网下载安装“Build Tools for Visual Studio 2022”,并确保安装时勾选“C++桌面开发”工作负载。或者,更简单的方法是直接使用预编译的Wheel文件,或考虑在WSL(Windows Subsystem for Linux)中运行Locust,后者体验更接近Linux。

3. 核心概念与第一个测试脚本解剖

安装好Locust,我们直接动手写第一个脚本。别担心,Locust的脚本结构非常清晰。我们先从一个最简单的例子开始,然后逐步增加复杂度。

3.1 理解Locust脚本的骨架:HttpUserTaskSet

创建一个名为locustfile.py的文件(这是Locust默认寻找的入口文件名)。写入以下内容:

from locust import HttpUser, task, between class QuickstartUser(HttpUser): # 模拟用户在执行任务之间的等待时间,介于1到5秒之间 wait_time = between(1, 5) # 被测系统的根地址 host = "https://httpbin.org" @task def get_status(self): # self.client是HttpUser内置的HttpSession实例,用法和requests库几乎一样 self.client.get("/get") @task(3) # 权重为3,意味着这个任务被执行的频率是上一个任务的3倍 def post_data(self): self.client.post("/post", json={"hello": "world"})

我们来拆解这个脚本的每一个部分:

  • HttpUser:这是模拟的“用户类”。每个并发的虚拟用户(Vuser)都是这个类的一个实例。host属性定义了这些用户将要攻击的目标系统基础URL。
  • wait_time:定义了用户思考时间。between(1, 5)表示每个任务执行后,用户会随机等待1到5秒再执行下一个任务。这非常重要,因为真实用户不是机器,不会毫不停歇地发送请求。忽略思考时间会导致压测结果过于理想化,无法反映真实负载。除了between,还有constant(固定间隔)和constant_pacing(固定节奏)等策略。
  • @task装饰器:用来标记一个方法是用户要执行的任务。一个用户类里可以有多个task
  • 任务权重@task装饰器可以接受一个整数参数,如@task(3),代表该任务的权重。在默认情况下,每个任务权重为1。Locust会按权重比例随机选择下一个要执行的任务。在上面的例子中,post_data任务被选中的概率是get_status任务的3倍。
  • self.client:这是HttpUser内置的一个HttpSession对象,它实际上是requests.Session的子类。所以你熟悉的get,post,put,delete等方法都可以直接使用,并且自动保持cookies(会话状态)。这是Locust易用性的关键。

3.2 让脚本更真实:参数化、关联与验证

上面的脚本太简单了,真实业务往往需要登录、携带Token、处理动态参数。下面我们写一个更贴近现实的例子:模拟用户登录后查询个人订单列表。

from locust import HttpUser, task, between import random class EcommerceUser(HttpUser): wait_time = between(2, 5) host = "https://api.demo-shop.com" def on_start(self): """每个虚拟用户开始运行时只执行一次,常用于登录""" login_payload = {"username": f"test_user_{random.randint(1,1000)}", "password": "default_password"} with self.client.post("/auth/login", json=login_payload, catch_response=True) as response: if response.status_code == 200: response_data = response.json() # 将登录返回的token保存在用户实例中,供后续请求使用 self.token = response_data.get("access_token") response.success() else: response.failure(f"Login failed: {response.text}") @task(2) def view_orders(self): """查看订单列表,需要认证""" if hasattr(self, 'token'): headers = {"Authorization": f"Bearer {self.token}"} with self.client.get("/api/v1/orders", headers=headers, name="/orders", catch_response=True) as response: # 对响应进行内容校验,而不仅仅是状态码 if response.status_code == 200 and "order_list" in response.text: response.success() else: response.failure(f"Unexpected response: {response.status_code} - {response.text[:100]}") else: print("User not logged in, skipping task.") @task(1) def view_product_detail(self): """浏览商品详情页,这个接口可能不需要登录""" product_id = random.choice([1001, 1002, 1003, 1004, 1005]) # 使用`name`参数对同一模式的URL进行分组统计,否则每个不同的product_id都会被单独统计,导致报表混乱。 self.client.get(f"/api/v1/products/{product_id}", name="/products/[id]") def on_stop(self): """每个虚拟用户停止运行时执行,可用于登出(非必需)""" # 如果业务有登出接口,可以在这里调用 # self.client.post("/auth/logout") pass

这个脚本的进阶要点解析:

  1. on_starton_stop方法:这是HttpUser的生命周期钩子。on_start在每个虚拟用户实例启动时调用一次,非常适合执行登录操作,并将登录凭证(如token)保存为实例变量(self.token)。on_stop在用户停止时调用,可用于清理。
  2. 响应验证 (catch_response=True):默认情况下,Locust仅根据HTTP状态码(2xx)判断请求成功与否。但在实际业务中,状态码200可能返回的是错误信息JSON。通过with self.client.request(..., catch_response=True) as response:上下文管理器,我们可以手动控制成功/失败的判定。调用response.success()response.failure()来告诉Locust结果。这是定位接口逻辑错误的关键
  3. 动态参数与数据池:使用random、从文件或数据库中读取数据来实现请求参数的动态化,避免所有用户请求完全一样的数据,这更符合真实场景。例如,从CSV文件中读取用户名密码对。
  4. 请求命名 (name参数):注意view_product_detail任务中的name="/products/[id]"。如果不加这个参数,Locust的统计报表会为每个不同的product_id(如/products/1001,/products/1002)生成单独的条目,导致报表冗长且无法聚合。加上name参数后,所有这些请求会被归为一类进行统计,报表清晰得多。

踩坑实录:早期我做压测时,曾忘记使用name参数对相似请求进行分组,结果在模拟100种商品ID时,Web UI的统计列表长达100行,根本没法看。另一个坑是,没有使用catch_response做内容校验,导致接口明明返回了{"code": 500, "msg": "库存不足"},Locust却还显示请求成功,误导了性能判断。务必记住:性能测试的前提是功能正确

4. 执行测试与Web UI深度解读

脚本写好了,现在让我们启动Locust,看看它的威力。Locust有两种运行模式:Web UI模式(交互式)和无头模式(命令行),我们分别介绍。

4.1 启动测试与Web UI交互

在脚本所在目录打开终端,确保虚拟环境已激活,运行:

locust -f locustfile.py

默认情况下,Locust会启动一个本地Web服务器,地址是http://localhost:8089。用浏览器打开它。

你会看到Locust的启动界面,需要填写几个参数:

  • Number of users (peak concurrency):要模拟的总用户数(峰值并发数)。Locust会以你设置的孵化速率(Spawn rate)逐步增加到这个数量。
  • Spawn rate (users started/second):孵化速率,每秒启动多少个虚拟用户。设置为10,意味着Locust会每秒创建10个用户,直到达到总用户数。不要一开始就设置很高的孵化速率,这会给目标系统一个“软启动”的过程,便于观察系统负载逐步上升时的表现。
  • Host:目标系统的根URL。如果脚本里已经设置了host属性,这里可以覆盖。

填写后,点击“Start swarming”,压测就开始了。页面会自动跳转到数据统计页。

4.2 读懂核心数据仪表板

Web UI是Locust的精华,它提供了实时的性能数据可视化。你需要重点关注以下几个标签页和指标:

1. Statistics(统计摘要)这是最重要的表格。它展示了所有请求类型的聚合性能数据。

列名含义与解读
Type请求路径(由name参数定义)。
Name请求的显示名称。
Requests总请求数。
Fails失败请求数。这是你需要第一时间关注的指标,任何非零失败都需要排查。
Median (ms)中位数响应时间。50%的请求响应时间低于这个值。这个值比平均响应时间更能抵抗异常值(慢请求)的干扰,更能代表“典型”用户体验。
90%ile (ms)90分位响应时间。90%的请求响应时间低于这个值。这是评估系统性能的黄金指标之一,它告诉你绝大多数用户(90%)的体验上限。例如,90%ile为1200ms,意味着90%的用户感觉页面在1.2秒内加载完成。
95%ile (ms)95分位响应时间。要求更严苛的场景会看这个。
99%ile (ms)99分位响应时间。用于评估长尾效应,极端慢的请求有多慢。
Average (ms)平均响应时间。仅供参考,容易被少数慢请求拉高。
Min/Max (ms)最小/最大响应时间。
Average size (bytes)平均响应大小。
Current RPS当前每秒请求数。系统实时吞吐量。
Current Failures/s当前每秒失败数。

2. Charts(图表)

  • Total Requests per Second:总RPS随时间变化曲线。理想情况下,随着用户数增加,RPS应平稳上升后趋于稳定。如果曲线出现剧烈抖动或下降,说明系统可能出现了瓶颈(如数据库连接池耗尽、线程阻塞)。
  • Response Times (ms):响应时间分位数(中位数、95%ile)随时间变化曲线。随着负载增加,响应时间会缓慢上升是正常的,但如果是指数级飙升,则表明系统已过载。
  • Number of Users:活跃用户数曲线。可以对照此曲线观察RPS和响应时间的变化。

3. Failures(失败详情)这里列出了所有失败的请求,包括错误类型(如ConnectionError、Timeout)、发生时间和具体的错误信息。这是你排查问题的第一现场。经常点开看看,能快速发现是网络问题、服务超时还是接口逻辑错误。

4. Exceptions(异常)脚本运行过程中抛出的Python异常会在这里显示。

5. Download Data测试结束后,你可以在这里下载CSV格式的完整报告数据,用于更深入的分析或生成自定义图表。

实操心得:我通常的压测流程是:1) 设置一个较低的用户数和孵化速率(如50用户,5每秒),运行2-3分钟,看基础功能是否通,错误率是否为0。2) 逐步增加负载(如200,500,1000用户),观察RPS和响应时间曲线的变化拐点。那个让响应时间开始陡增、RPS不再增长的“点”,就是系统当前的性能瓶颈点。记录下这个点的并发用户数和RPS,它就是系统容量的重要参考。

5. 高级配置与分布式压测实战

当你要模拟成千上万的并发用户时,单台负载生成器(运行Locust的机器)可能成为瓶颈(受限于CPU、网络或端口数)。这时就需要使用Locust的分布式模式。

5.1 分布式压测架构解析

Locust采用主从(Master-Worker)架构:

  • Master节点:负责协调、分发测试任务、收集汇总所有Worker的数据,并托管Web UI。Master本身不模拟任何用户
  • Worker节点:负责实际生成负载、执行测试脚本中的用户任务。可以启动多个Worker,甚至可以跨多台物理机。

为什么需要分布式?

  1. 突破单机资源限制:单机操作系统有端口数、线程/协程数限制,模拟大量用户时可能无法创建足够连接。
  2. 生成足够压力:对于高性能的后端服务,需要多台机器同时发压才能达到其瓶颈。
  3. 模拟地理分布的用户:可以从不同地域的云服务器发起请求,更真实地模拟全球用户的网络延迟。

5.2 搭建分布式集群步骤

假设你有三台机器,IP分别为192.168.1.10(Master),192.168.1.11(Worker1),192.168.1.12(Worker2)。

步骤1:准备环境确保三台机器上都安装了相同版本的Locust,并且拥有完全相同的测试脚本(locustfile.py)及其依赖(如自定义的Python模块、数据文件)。可以通过Git同步或手动拷贝。

步骤2:启动Master节点在Master机器(192.168.1.10)上执行:

locust -f locustfile.py --master --host=https://your-target.com

--master参数指定此节点为Master。它会启动Web UI(默认8089端口)并等待Worker连接。

步骤3:启动Worker节点分别在两台Worker机器上执行:

# 在 Worker1 (192.168.1.11) 上 locust -f locustfile.py --worker --master-host=192.168.1.10 # 在 Worker2 (192.168.1.12) 上 locust -f locustfile.py --worker --master-host=192.168.1.10

--worker参数指定此节点为Worker。--master-host告诉Worker Master节点的地址。

步骤4:在Web UI中控制测试现在,打开Master的Web UI (http://192.168.1.10:8089)。你会看到界面和单机模式一样,但底部的“WORKERS”区域会显示已连接的Worker数量(应为2)。此时,你设置的“Number of users”将是所有Worker共同模拟的总用户数。例如,你设置1000用户,Locust Master会协调两个Worker,每个大约模拟500个用户。

5.3 关键配置参数与实战技巧

除了--master--worker,Locust命令行还有很多实用参数:

  • -f, --locustfile: 指定测试脚本路径。
  • --host: 设置目标系统主机。
  • --headless: 无头模式,不启动Web UI,适用于CI/CD。需配合--users--spawn-rate使用。
    locust -f locustfile.py --headless --users 1000 --spawn-rate 100 --run-time 5m --host=https://your-target.com
    这条命令会直接启动压测,模拟1000用户,以每秒100的速率启动,运行5分钟后自动停止并输出摘要报告。
  • --run-time: 在无头模式下指定测试运行时长,如5m(5分钟)、1h30m
  • --csv: 在无头模式下将结果导出为CSV文件前缀。
    locust -f locustfile.py --headless --users 1000 --spawn-rate 100 --run-time 2m --csv=result
    这会生成result_stats.csv,result_failures.csv等文件。
  • -L, --loglevel/--logfile: 控制日志级别和输出到文件,调试时非常有用。
  • --expect-workers: Master启动时等待指定数量的Worker连接后再开始测试,用于自动化脚本。

分布式压测避坑指南

  1. 时钟同步:确保所有Master和Worker机器的系统时间基本同步(使用NTP),否则报表中的时间戳会混乱。
  2. 防火墙与网络:确保Master和Worker之间能互相通信(默认端口为5557和5558)。在云环境或跨网段时,需要配置安全组或防火墙规则。
  3. 资源监控:压测时,不仅要监控被测服务,也要监控Locust Worker机器本身的CPU、内存、网络带宽。如果Worker资源耗尽,会成为瓶颈,导致压测数据不准。我常用htopiftop来实时监控。
  4. 测试数据隔离:如果脚本中使用到了像self.token这样的实例变量,要确保不同Worker上的用户数据不会冲突(例如,使用不同的用户池文件)。或者,更推荐使用独立数据源,比如每个Worker从不同的数据库分片或不同范围的ID中读取数据。

6. 性能测试结果分析与瓶颈定位思路

压测跑完了,面对一大堆数据,该如何分析,如何定位瓶颈?这比单纯执行压测更重要。

6.1 核心性能指标解读与健康度评估

一份健康的压测报告应该是什么样子?我们结合指标来看:

  1. 失败率 (Failure Rate)必须为0或低于可接受阈值(如0.1%)。任何非预期的失败都意味着功能或性能问题,需要优先排查。高失败率下的性能数据没有意义。
  2. 响应时间 (Response Time)
    • 中位数 (Median):应保持相对稳定且处于较低水平(例如,核心接口<200ms)。
    • 90%/95%分位数 (90%ile/95%ile):这是服务稳定性的关键。它们应该随负载平缓上升,但不应与中位数拉开巨大差距。如果95%ile远高于中位数(例如,中位数50ms,95%ile 2000ms),说明存在长尾请求,可能是某些请求触发了慢查询、缓存失效或资源竞争。
    • 观察曲线:在负载增加过程中,响应时间曲线应呈平缓的“L”型或缓慢上升的斜坡。如果出现近乎垂直的飙升,就是明确的性能拐点。
  3. 吞吐量 (Throughput / RPS)
    • 随着并发用户数增加,RPS应线性或接近线性增长。
    • 当达到系统瓶颈时,RPS会达到一个峰值并趋于平稳,即使再增加用户数,RPS也不再增长甚至下降。这个峰值RPS就是系统在当前场景下的最大处理能力。
    • RPS与响应时间关联分析:理想情况下,RPS平稳,响应时间平稳。如果RPS抖动剧烈,通常意味着系统内部(如数据库、外部API)不稳定。

6.2 常见性能瓶颈模式与排查清单

当指标出现异常时,可以按照以下清单,自顶向下进行排查:

现象可能瓶颈点排查工具与方法
响应时间随负载线性增长,RPS增长缓慢应用服务器资源不足监控服务器CPU、内存使用率。工具:top,htop,vmstat
响应时间在某一负载下突然飙升,RPS上不去或下降数据库瓶颈1. 监控数据库连接数、慢查询日志。
2. 检查是否有锁竞争、全表扫描。
3. 工具:数据库自身的监控(如SHOW PROCESSLIST)、pt-query-digest
大量连接超时、连接被拒绝错误中间件/Web服务器连接池耗尽1. 检查Nginx/Apache的并发连接数、活动连接数配置。
2. 检查应用服务器(如Tomcat、Gunicorn)的线程池/Worker数配置。
3. 工具:`netstat -an
CPU使用率不高,但响应时间慢I/O等待(磁盘或网络)1. 使用iostatiotop查看磁盘利用率、等待时间。
2. 检查是否有大量日志写入、文件操作。
3. 检查外部API或下游服务的响应时间。
内存使用率持续增长,直至OOM内存泄漏1. 使用jstat(Java)、memory-profiler(Python)等工具分析内存堆栈。
2. 检查是否有未关闭的连接、缓存无限增长、大对象未释放。
RPS和响应时间周期性波动垃圾回收(GC)缓存失效风暴1. 查看GC日志(Java),分析Full GC频率和时长。
2. 检查缓存过期策略,是否在某一时刻大量缓存同时失效,导致请求穿透到数据库。

一个实战分析案例: 我曾压测一个订单提交接口。在并发500以下时,一切正常。当并发达到800时,95%ile响应时间从200ms猛增到5秒,同时RPS不再增长。数据库监控显示CPU使用率仅40%,但磁盘I/O等待队列很长。进一步检查慢查询日志,发现有一条更新库存的SQL,在并发高时发生了大量的行锁等待。解决方案不是升级硬件,而是优化业务逻辑,将库存扣减改为更高效的乐观锁或队列异步处理。这个案例说明,瓶颈往往在代码和架构层面,而不是资源层面。

7. 集成CI/CD与生成专业报告

性能测试不应该是一次性的活动,而应该融入开发流程。我们可以将Locust集成到CI/CD流水线中,每次代码变更后自动运行基准测试,防止性能回归。

7.1 使用无头模式与CI工具集成

以GitLab CI为例,可以在.gitlab-ci.yml中配置一个性能测试阶段:

stages: - test - performance performance_test: stage: performance image: python:3.9-slim # 使用官方Python镜像 before_script: - pip install locust script: # 1. 启动被测服务(假设是docker-compose项目) - docker-compose up -d - sleep 30 # 等待服务启动 # 2. 运行Locust压测(无头模式),设置一个合理的并发和时长 - locust -f locustfile.py --headless --users 100 --spawn-rate 10 --run-time 1m --host=http://localhost:8000 --csv=perf_results --html=perf_report.html after_script: # 3. 将生成的报告保存为CI制品 - cp perf_report.html ${CI_PROJECT_DIR}/ artifacts: paths: - perf_report.html - perf_results*.csv when: always # 即使测试失败也保留报告 only: - master # 仅在合并到主分支时运行,或根据标签触发

在这个配置中,流水线会自动安装Locust,启动服务,运行1分钟的压测,并将生成的HTML报告和CSV数据保存为制品,供后续查看和分析。

7.2 生成与美化HTML报告

Locust无头模式运行后生成的perf_report.html比较简陋。我们可以使用第三方库来生成更美观的报告,例如locust-plugins提供的--html选项(已在上面示例中使用)生成的报告就比默认的文本摘要好很多。

对于更高级的需求,可以将CSV结果导入到Grafana、Prometheus等监控系统进行可视化,或者用Python的pandasmatplotlib库进行自定义分析。

# 一个简单的数据分析脚本示例 (analysis.py) import pandas as pd import matplotlib.pyplot as plt # 读取Locust生成的统计CSV df_stats = pd.read_csv('perf_results_stats.csv') df_stats = df_stats[df_stats['Name'].str.contains('API')] # 筛选关键接口 # 绘制响应时间趋势(假设CSV中有时间序列数据,通常需要从时间序列日志中获取) # 这里仅作示例,实际Locust的_stats.csv是聚合数据,时间序列数据需要额外配置输出 plt.figure(figsize=(10, 6)) for index, row in df_stats.iterrows(): plt.plot([1, 2, 3], [row['Median Response Time'], row['90%ile'], row['95%ile']], marker='o', label=row['Name']) plt.xlabel('Load Stage') plt.ylabel('Response Time (ms)') plt.title('API Response Time under Different Loads') plt.legend() plt.grid(True) plt.savefig('response_times.png') print("分析图表已生成: response_times.png")

7.3 制定性能验收标准与告警

在CI中集成性能测试后,关键的一步是设定性能基线验收标准。例如:

  • 核心接口的95%ile响应时间不得高于300ms。
  • 在基准负载(如100并发)下,错误率必须为0。
  • 压测期间的服务器CPU平均使用率不得高于70%。

你可以在CI脚本中加入检查逻辑,如果这些标准不达标,就让流水线失败,从而阻止性能退化的代码合并。

# 一个简单的Bash检查示例,在CI脚本中 FAILURE_COUNT=$(grep -c \"Fails\" perf_results_stats.csv | awk '{sum+=$2} END {print sum}') if [ $FAILURE_COUNT -gt 0 ]; then echo "性能测试存在失败请求!" exit 1 fi # 使用jq解析JSON输出(如果使用--json报告)检查响应时间 # 这里假设有工具能提取95%ile值 P95_RESPONSE_TIME=$(some_tool_to_extract_p95) if [ $(echo "$P95_RESPONSE_TIME > 300" | bc) -eq 1 ]; then echo "95分位响应时间 ${P95_RESPONSE_TIME}ms 超过阈值300ms" exit 1 fi

将性能测试左移,变成开发流程中自动化的守门员,是保障线上系统稳定性的有效手段。Locust凭借其代码化和易集成的特性,非常适合扮演这个角色。