1. 项目概述:为什么选择Locust进行接口性能测试?
如果你正在寻找一个能让你用代码定义用户行为、轻松模拟成千上万并发请求,并且报告还足够直观的性能测试工具,那么Locust大概率会成为你的首选。我最早接触性能测试是从JMeter开始的,图形化界面确实友好,但当你需要测试一个复杂的、有状态依赖的业务流(比如用户登录后,再查询个人订单,最后完成支付)时,用JMeter配置起来就有些繁琐了。后来转向了Locust,它“用代码定义一切”的理念,让我感觉找回了编程的掌控感。Locust本质上是一个用Python写的开源负载测试框架,它的核心思想是“定义用户行为,然后放出一大群这样的用户去攻击你的系统”。这里的“用户”,在Locust里就是一个Python类,它的行为完全由你来编写,这为模拟真实、复杂的业务场景提供了极大的灵活性。
那么,它具体能解决什么问题呢?首先,是并发模拟的真实性。很多工具模拟的并发是“线程级”或“进程级”的,管理和资源消耗是个问题。Locust基于协程(通过gevent实现),可以用单进程轻松模拟数千甚至上万的并发用户,对测试机资源消耗相对较小。其次,是测试场景的灵活性。你不再需要拖拽各种元件,而是直接编写Python代码来描述用户从打开应用到最后退出的完整操作序列,包括思考时间、分支逻辑、数据参数化等。最后,是结果的可观测性。Locust提供了一个实时的Web UI界面,你可以清晰地看到当前的RPS(每秒请求数)、响应时间、失败率等关键指标,并且所有数据都可以导出进行进一步分析。
这篇文章,我会以一个资深测试开发的角度,带你彻底吃透Locust,特别是其最核心的两个概念:Locust类和TaskSet类。我会详细拆解它们的原理、用法、配合技巧,并分享我在实际压测中积累的大量“坑点”和“骚操作”。无论你是刚接触性能测试的新手,还是想从其他工具迁移过来的老手,这篇内容都能让你不仅会用Locust,更能理解其设计哲学,从而设计出更精准、更有效的性能测试方案。
2. Locust核心架构与设计哲学深度解析
在开始写第一行测试脚本之前,理解Locust的架构设计至关重要。这能帮助你在后续遇到复杂场景时,知道该从哪个方向去思考和解决问题,而不是盲目地复制粘贴代码。
2.1 事件驱动与协程:高并发的基石
Locust的高并发能力并非来自多线程或多进程,而是基于事件驱动和协程。它底层使用了gevent这个协程库。简单来说,你可以把协程理解为“微线程”。对于操作系统而言,一个Locust测试进程可能只对应几个真正的操作系统线程,但在这个进程中,它可以创建成千上万个协程。
每个模拟的“用户”(即一个Locust类的实例)都在一个独立的协程中运行。当一个用户发出一个HTTP请求后,这个协程不会傻傻地等待网络响应(这会阻塞),而是会立即“让出”执行权,切换到其他就绪的协程去执行其他用户的任务。当网络响应返回时,这个协程再被唤醒继续执行。这种“异步非阻塞”的模式,使得单机模拟海量并发用户成为可能,且资源开销远小于传统的多线程模型。
注意:正因为这种协程模型,你在编写
task(任务)方法时,必须使用Locust提供的HttpUser客户端(如self.client.get)来发起请求。如果你使用了标准的requests库或其他同步HTTP客户端,那么这个请求会阻塞整个协程,甚至整个事件循环,导致并发数急剧下降,性能测试结果完全失真。这是新手最容易踩的第一个大坑。
2.2 Locust类:模拟用户的蓝本
Locust类(在最新版本中,更常用的是其子类HttpUser)是每个模拟用户的蓝本。你可以把它理解为一个“用户模板”。当Locust启动并指定要模拟1000个用户时,它就会创建1000个这个Locust类的实例。每个实例都是独立的,拥有自己的状态(比如self.client维护的会话、自定义的属性等)。
Locust类的核心职责是定义两件事:
wait_time:用户在每个任务执行后的等待时间。这用于模拟真实用户操作间的停顿。Locust内置了几种策略:between(min, max):在最小值和最大值之间随机等待。constant(wait_time):每次等待固定时间。constant_pacing(interval):确保每个任务执行周期(任务执行时间+等待时间)至少为指定的间隔,常用于控制恒定RPS。
tasks:用户要执行的任务列表。这是Locust类的灵魂。tasks属性通常是一个列表,里面包含了该用户可能执行的所有任务。任务可以是一个普通的Python可调用对象(函数),也可以是TaskSet类。
这里有一个关键设计:任务权重。你可以通过给tasks列表传入一个元组(callable, weight)来定义不同任务被执行的相对概率。例如:
from locust import HttpUser, task, between class MyUser(HttpUser): wait_time = between(1, 3) @task(3) # 权重为3 def view_item(self): self.client.get("/item/123") @task(1) # 权重为1 def view_cart(self): self.client.get("/cart") # 等价于 tasks = [view_item, view_cart] 但无法定义权重 # 更灵活的方式是使用 tasks 属性列表: # tasks = [(view_item, 3), (view_cart, 1)]在这个例子中,view_item任务被选中的概率是view_cart的3倍。Locust会按照权重比例随机选择下一个要执行的任务。
2.3 TaskSet类:复杂用户行为的组织者
当你的用户行为不是简单的一两个独立接口调用,而是一个有顺序、有分支、甚至可嵌套的复杂流程时,TaskSet类就派上用场了。TaskSet可以理解为“任务集”,它允许你将一组相关的任务组织在一起,并定义它们之间的执行逻辑。
TaskSet的核心价值在于状态保持和流程封装。想象一个电商场景:用户需要先登录(获取token),然后用这个token去访问个人中心、添加商品到购物车、下单。登录状态(token)需要在后续一系列任务中共享。如果只用@task装饰的独立方法,维护这个状态会非常麻烦。而TaskSet实例有自己的self.client和self.parent(指向所属的Locust用户实例),可以很方便地在内部任务间共享数据。
TaskSet的执行模型:当一个TaskSet被Locust用户选中执行时,这个用户会“进入”该TaskSet。在TaskSet内部,它会根据定义的任务和权重,循环执行其中的任务,直到被中断(通过调用self.interrupt())或者达到了执行次数限制(如果设置了)。执行完一个TaskSet后,控制权会返回给父级(可能是另一个TaskSet,也可能是Locust用户本身),由父级决定下一个要执行的任务。
嵌套TaskSet:这是模拟复杂场景的利器。例如,你可以定义一个BrowseTaskSet(浏览商品集)和一个PurchaseTaskSet(购买流程集)。然后在主用户类中,让用户有一定概率进入“浏览模式”,有一定概率进入“购买模式”。
from locust import HttpUser, TaskSet, task, between class BrowseTaskSet(TaskSet): @task def view_homepage(self): self.client.get("/") @task def search_product(self): self.client.get("/search?q=book") @task def exit_browse(self): self.interrupt() # 关键:中断当前TaskSet,返回父级 class PurchaseTaskSet(TaskSet): def on_start(self): self.login() # 进入购买流程前先登录 def login(self): # ... 登录逻辑,获取token并存到self.client.headers中 pass @task def add_to_cart(self): self.client.post("/cart", json={"item_id": 123}) @task def checkout(self): self.client.post("/order") @task def exit_purchase(self): self.interrupt() class WebsiteUser(HttpUser): wait_time = between(2, 5) tasks = [BrowseTaskSet, PurchaseTaskSet] # 直接引用TaskSet类 # 这里BrowseTaskSet和PurchaseTaskSet的权重相同,各50%概率被选中进入在这个结构里,一个WebsiteUser实例启动后,会随机选择进入BrowseTaskSet或PurchaseTaskSet。如果进入BrowseTaskSet,它会在浏览任务中循环,直到执行了exit_browse任务(调用self.interrupt())才会跳出,然后下次任务选择可能又进入PurchaseTaskSet。这样就形成了一个动态的、贴近真实用户的行为模型。
3. 从零到一构建Locust性能测试脚本实战
理解了核心概念,我们动手写一个完整的、贴近真实项目的测试脚本。我们将模拟一个简化的论坛系统的用户行为:用户随机浏览帖子列表,点击进入帖子详情,并且有较小概率发表新帖。
3.1 环境准备与Locust安装
首先,确保你有一个Python环境(3.6及以上)。强烈建议使用虚拟环境(venv或conda)来管理依赖。
本地安装Locust(这也是当前网络上的一个热点)非常简单:
pip install locust这条命令会安装Locust及其核心依赖(如gevent, flask, requests等)。验证安装:
locust -V如果输出版本号(如locust 2.20.0),说明安装成功。
实操心得:在团队协作或CI/CD流水线中,我习惯将依赖写入
requirements.txt文件。除了locust,根据项目需要,你可能还需要安装psutil(用于更精确的资源监控)或locust-plugins(社区扩展插件)。对于需要测试HTTPS且证书不受信任的内部系统,可能还需要在脚本中全局禁用SSL警告,但这在生产环境测试中需谨慎评估安全风险。
3.2 编写第一个Locustfile:基础用户行为定义
创建一个名为locustfile.py的文件(这是Locust默认寻找的入口文件)。
from locust import HttpUser, task, between, TaskSet import random class ForumBehavior(TaskSet): """ 论坛用户行为任务集 模拟:浏览列表 -> 查看详情 -> (小概率)发帖 """ # 假设的帖子ID池,实际测试中可能从先前的响应中动态获取 post_ids = [1001, 1002, 1003, 1004, 1005] def on_start(self): """ 当用户进入这个TaskSet时执行,常用于登录或初始化 这里我们模拟一个简单的会话初始化,比如获取CSRF token(如果系统需要) """ # 示例:访问首页可能设置一些会话cookie with self.client.get("/", catch_response=True) as response: if response.status_code == 200: # 可以在这里解析响应,提取token等 # self.token = response.json().get('token') pass else: response.failure(f"Homepage failed with {response.status_code}") @task(5) # 权重高,用户大部分时间在浏览 def browse_post_list(self): """浏览帖子列表页""" # 模拟翻页,随机选择页码 page = random.randint(1, 5) with self.client.get(f"/api/posts?page={page}&size=10", name="/api/posts?page=[page]", catch_response=True) as response: # catch_response=True 允许我们自定义成功/失败的判断逻辑 if response.status_code == 200: # 可以进一步检查响应内容,比如json结构是否正确 try: data = response.json() if not isinstance(data.get('items'), list): response.failure("Response format error: items not a list") except JSONDecodeError: response.failure("Invalid JSON response") else: response.failure(f"Status code: {response.status_code}") @task(3) def view_post_detail(self): """随机查看一个帖子详情""" post_id = random.choice(self.post_ids) # 使用`name`参数对同一模式的不同URL进行聚合统计,否则每个不同的post_id都会单独统计,报告会杂乱 with self.client.get(f"/api/posts/{post_id}", name="/api/posts/[id]", catch_response=True) as response: if response.status_code != 200: response.failure(f"Failed to view post {post_id}") @task(1) # 权重低,发帖概率小 def create_new_post(self): """发表一个新帖子""" title = f"Test Post from Locust {random.randint(1000,9999)}" content = "This is the content generated by performance test." payload = { "title": title, "content": content, "category": random.choice(["tech", "life", "news"]) } # 假设发帖需要认证,header已在on_start或父级User中设置 with self.client.post("/api/posts", json=payload, name="/api/posts [POST]", catch_response=True) as response: if response.status_code == 201: # 假设创建成功返回201 # 成功创建后,可以将新的post_id加入池子,模拟动态数据(高级用法,需注意线程安全) # try: # new_id = response.json().get('id') # if new_id: # self.post_ids.append(new_id) # except: # pass pass else: response.failure(f"Create post failed: {response.status_code}, {response.text}") @task def stop(self): """一个特殊的任务,用于中断当前TaskSet,让用户有机会执行其他TaskSet或结束""" self.interrupt() class ForumUser(HttpUser): """ 论坛模拟用户类 """ # 用户在每个任务执行后,等待1到3秒,模拟思考时间 wait_time = between(1, 3) # 指定用户的任务集。这里只有一个ForumBehavior。 # 用户启动后,就会进入ForumBehavior,并按照其中定义的权重执行任务,直到被interrupt。 # 由于ForumBehavior内部有一个stop任务会调用interrupt,所以用户会跳出,然后因为tasks列表里只有它,又会再次进入,形成循环。 tasks = [ForumBehavior] # 可以在User级别设置host,这样所有请求都会基于这个host # host = "http://your-forum-api.com" def on_start(self): """ 每个模拟用户开始运行时执行一次,常用于登录。 注意:这是User的on_start,在TaskSet的on_start之前执行。 """ # 这里可以放置全局的用户初始化,比如登录获取全局token # login_response = self.client.post("/api/login", json={"username": "test", "password": "test"}) # self.client.headers.update({"Authorization": f"Bearer {login_response.json()['token']}"}) print(f"User {self.id} is starting...") def on_stop(self): """ 每个模拟用户停止运行时执行一次,常用于清理。 """ # 例如,登出 # self.client.post("/api/logout") print(f"User {self.id} is stopping...")脚本关键点解析:
name参数:在client.get/post中设置name至关重要。它用于在Locust的统计报告中聚合相同模式的请求。如果不设置name,那么/api/posts/1001和/api/posts/1002会被视为两个不同的请求,导致报告数据分散,难以分析。设置了name="/api/posts/[id]"后,它们会被聚合在一起。catch_response=True:这个参数允许你手动控制请求的成功与失败。默认情况下,HTTP状态码为200-399的请求被视为成功。但有些接口可能返回200但业务状态是错的。使用catch_response=True并结合with语句,你可以在代码块内检查响应内容,并通过response.success()或response.failure()来标记结果。wait_time:设置在HttpUser级别,这意味着每个任务(无论是哪个TaskSet里的任务)执行后,用户都会等待这个时间。它是控制负载模型(如并发用户数、RPS)的关键参数之一。on_start与on_stop:HttpUser和TaskSet都有这两个生命周期方法。HttpUser.on_start在每个虚拟用户实例启动时运行一次,适合做全局登录。TaskSet.on_start在用户每次进入该TaskSet时运行,适合做该任务集特定的初始化。
3.3 运行测试与Web UI监控
保存好locustfile.py后,打开终端,进入该文件所在目录,执行:
locust默认会启动Web UI在http://localhost:8089。如果你没有图形界面(比如在服务器上),或者想进行自动化测试,可以使用无头模式:
locust --headless -u 100 -r 10 -t 1m --host=http://your-test-server.com--headless: 无头模式,不启动Web UI。-u 100: 设置模拟的总用户数为100。-r 10: 设置每秒启动10个用户(爬升速率)。-t 1m: 测试运行时间为1分钟。--host: 指定被测试系统的基地址。也可以在脚本中通过HttpUser.host属性设置。
在Web UI中,你需要填写:
- Number of users: 模拟的总用户数。
- Spawn rate: 每秒启动的用户数。
- Host: 被测试系统的基URL。
点击“Start swarming”开始测试。你会看到实时更新的图表和统计信息。
4. Locust测试报告深度解读与性能分析
Locust提供的报告是性能分析的核心依据。很多人只看平均响应时间和失败率,这远远不够。我们需要像侦探一样,从数据中挖掘出系统的真实瓶颈。
4.1 Web UI界面核心指标解析
启动测试后,Web UI主界面主要包含以下几个部分:
Statistics(统计表格):
- Type: 请求名称(就是你设置的
name)。 - Requests: 总请求数。
- Fails: 失败请求数。
- Median, 95%, 99%: 响应时间的百分位数。这是比平均响应时间更重要的指标。中位数(50%)表示一半的请求快于这个值。95%分位数(95%)表示95%的请求响应时间在这个值以内。它反映了绝大多数用户的体验。如果95%值很高,说明系统存在拖慢少数请求的瓶颈(如慢查询、缓存失效)。
- Average, Min, Max: 平均、最小、最大响应时间。
- Average size: 平均响应大小。
- Current RPS: 当前每秒请求数。
- Current Failures/s: 当前每秒失败数。
- Type: 请求名称(就是你设置的
Charts(图表):
- Total Requests/s: 总RPS随时间变化曲线。观察是否达到预期负载,是否稳定。
- Response Times (ms): 响应时间(平均和百分位)随时间变化曲线。重点关注曲线是否随着时间推移而上升,这可能是内存泄漏、数据库连接池耗尽或外部服务降级的迹象。
- Number of Users: 并发用户数变化曲线。
Failures(失败详情):列出所有失败的请求,包括错误信息和发生时间,是排查问题的第一现场。
Exceptions(异常详情):显示测试脚本运行过程中抛出的Python异常。
4.2 导出数据与生成HTML报告
Web UI的数据是实时的,但测试结束后需要一份完整的报告进行归档和分析。Locust支持导出CSV数据。
在Web UI界面,有“Download Data”选项卡,可以下载requests.csv、failures.csv、exceptions.csv和stats.csv。其中stats.csv包含了聚合后的统计信息。
对于自动化测试,你可以在命令行使用--csv参数来在运行结束时自动生成CSV报告:
locust --headless -u 100 -r 10 -t 2m --host=http://your-test-server.com --csv=report这会生成report_stats.csv、report_failures.csv等文件。
此外,社区有locust-plugins等插件可以生成更美观的HTML报告,或者你可以自己用pandas和matplotlib对CSV数据进行深度分析和可视化。
4.3 基于报告的性能瓶颈分析思路
拿到报告后,如何分析?
看失败率:如果失败率(Fails/Requests)超过0%(在严格场景下),就需要立即排查。查看
failures.csv,定位是哪些接口、在什么时间点、因为什么原因(超时?5xx错误?业务逻辑失败?)失败的。看响应时间百分位:不要只看平均值。如果95%分位响应时间是中位数的3倍以上,说明系统处理存在严重的长尾效应。可能的原因包括:
- 数据库慢查询:某些复杂查询或未命中的索引。
- 缓存穿透/击穿:大量请求同时查询一个不存在或过期的缓存键,直接打到数据库。
- 外部服务依赖不稳定:调用第三方API偶尔超时。
- 垃圾回收(GC):在Java/.NET等环境中,Full GC会导致所有线程暂停。
看RPS曲线与用户数曲线:
- RPS上不去:当并发用户数增加时,RPS是否线性增长?如果达到一个平台后不再增长,甚至下降,说明系统已经达到瓶颈。可能是应用服务器CPU/内存瓶颈、数据库连接数不足、线程池满等。
- RPS波动大:曲线呈锯齿状,可能表明系统在频繁地进行GC,或者有定时任务干扰。
对比不同接口的响应时间:找出整个业务链路中最慢的接口(“短板”)。优化往往从最慢的环节入手收益最大。
结合系统监控:性能测试一定要配合被测系统的监控(如CPU、内存、磁盘I/O、网络I/O、数据库监控、应用链路追踪等)。当Locust报告显示响应时间变慢时,去查看对应时间点的系统监控指标,才能准确定位瓶颈是在应用层、数据库层还是网络层。
5. 高级技巧与实战中常见问题排查
掌握了基础用法和报告解读,下面分享一些能让你事半功倍的高级技巧和那些“只有踩过坑才知道”的排查经验。
5.1 参数化与测试数据管理
静态的测试数据(如上面写死的post_ids)很快会耗尽,或者导致缓存过热,测试不真实。我们需要动态的、参数化的数据。
方案一:从文件中读取(适合数据量不大)
import csv from locust import HttpUser, task, between class ApiUser(HttpUser): wait_time = between(1, 2) def on_start(self): # 在用户启动时,为其分配独立的数据队列 with open('test_data.csv', 'r') as f: reader = csv.DictReader(f) self.data_queue = list(reader) # 注意:简单列表在协程中可能不是线程安全的,但对于只读或每个用户独立一份数据是安全的。 random.shuffle(self.data_queue) self.data_index = 0 @task def use_data(self): if not self.data_queue: self.environment.runner.quit() # 数据用完停止测试 return data = self.data_queue[self.data_index % len(self.data_queue)] self.data_index += 1 self.client.post("/api/endpoint", json=data)方案二:使用队列(Queue)实现全局共享数据池(适合高并发下数据不重复)
import queue from locust import HttpUser, task, between, events # 在测试开始前初始化全局数据队列 test_data_queue = queue.Queue() @events.test_start.add_listener def on_test_start(environment, **kwargs): """在测试开始时,将数据灌入队列""" print("Loading test data...") with open('large_data_set.csv', 'r') as f: reader = csv.DictReader(f) for row in reader: test_data_queue.put(row) print(f"Test data loaded. Total: {test_data_queue.qsize()}") class ApiUser(HttpUser): wait_time = between(0.5, 1) @task def consume_data(self): try: # 非阻塞获取数据,如果队列为空,则标记用户任务完成 data = test_data_queue.get_nowait() # 使用data发起请求... self.client.post("/api/process", json=data) # 可选:处理成功后,将数据放回队列尾部以实现循环使用 # test_data_queue.put(data) except queue.Empty: # 数据用完,这个用户停止执行新任务 # 也可以选择让用户停止:self.stop(True) print(f"User {self.id}: No more data.")注意事项:多协程(用户)访问共享资源(如全局列表、字典)存在数据竞争风险。
queue.Queue是线程/协程安全的,是首选。如果必须用列表或字典,可以考虑使用gevent的锁(from gevent.lock import Semaphore),但会引入性能损耗和死锁风险。
5.2 处理Cookie、Session与Token认证
大多数现代应用使用Token(如JWT)认证。Locust的HttpUser.client是一个requests.Session的子类,会自动处理Cookie。
对于Token认证,通常做法是在on_start中登录并设置请求头:
class AuthenticatedUser(HttpUser): host = "https://api.example.com" def on_start(self): # 登录获取token resp = self.client.post("/auth/login", json={"username": "test", "password": "pass"}) if resp.status_code == 200: self.token = resp.json()["access_token"] # 为后续所有请求设置Authorization头 self.client.headers.update({"Authorization": f"Bearer {self.token}"}) else: # 登录失败,记录错误并停止此用户 resp.failure("Login failed") self.stop(True) # 停止这个用户实例 @task def access_protected_resource(self): # 请求会自动带上Authorization头 self.client.get("/api/protected")对于需要维护会话(Session)的系统,self.client会自动管理Cookies,你无需手动处理,就像浏览器一样。
5.3 常见问题排查与调试技巧
RPS达不到预期,但CPU/内存使用率很低:
- 检查
wait_time:可能设置得太长,导致用户大部分时间在等待。减少等待时间或使用constant_pacing来控制节奏。 - 检查被测试服务:是否应用服务器有并发连接数限制?是否用了同步阻塞的框架?用
netstat或ss命令查看测试机到服务端的连接数是否上得去。 - 检查Locust运行模式:默认是单进程。如果测试机性能很强,可以尝试使用
--master/--worker多进程模式,让多个Worker进程来生成负载。启动命令示例:# 终端1:启动Master节点(负责收集数据、提供Web UI) locust --master # 终端2/3...:启动Worker节点(负责产生负载) locust --worker --master-host=localhost
- 检查
出现大量“Connection refused”或“Timeout”错误:
- 检查目标服务是否存活,以及网络连通性。
- 检查目标服务的最大文件描述符限制和连接数限制。Linux下使用
ulimit -n查看,服务端(如Nginx、Tomcat)也有最大连接数配置。 - 调整Locust的HTTP客户端超时设置(默认未设置,可能导致挂起):
class MyUser(HttpUser): # 设置连接、读取超时 network_timeout = 30.0 # 连接超时 connection_timeout = 30.0 # 读取超时 # 或者直接在client请求中设置 # self.client.get("/url", timeout=(10, 30)) # (连接超时, 读取超时)
测试结果波动很大,不稳定:
- 确保测试环境独立,避免其他作业干扰。
- 进行预热(Warm-up):在正式测试前,先以低负载运行一段时间,让JVM、数据库缓存等热起来。可以在脚本中通过
@events.test_start.add_listener触发一个预热任务。 - 延长测试时间:短时间测试(如30秒)容易受到偶然因素影响。性能测试通常需要持续运行至少5-10分钟,取稳定后的数据。
- 检查外部依赖:你的服务是否依赖了其他不稳定的第三方API或数据库?
如何调试测试脚本本身:
- 在任务方法中多使用
print语句(输出会显示在Locust控制台或Worker日志中)。 - 使用Python的日志模块,配置适当的日志级别。
- 对于复杂的逻辑,可以先用单个用户(
-u 1)运行,观察其执行流程是否正确。
- 在任务方法中多使用
5.4 分布式执行与云上压测
当需要模拟数万甚至更高并发时,单机Locust可能成为瓶颈。此时需要分布式执行。
核心概念:一个Master节点和多个Worker节点。Master不模拟用户,只负责分发测试任务、收集结果、提供Web UI。Worker节点负责实际生成负载。
部署步骤:
- 准备多台性能良好的机器作为Worker(可以是物理机、虚拟机或容器)。
- 在所有机器上安装相同版本的Locust和测试脚本。
- 在一台机器上启动Master:
locust --master。 - 在每台Worker机器上启动Worker,并指向Master的IP:
locust --worker --master-host=<master_ip>。 - 在Master的Web UI上启动测试,Master会自动将负载分配至所有Worker。
云上压测建议:为了更真实地模拟来自不同地域的用户,可以在不同的云可用区(Availability Zone)或地域(Region)部署Worker节点。同时,要确保Master节点与Worker节点之间、Worker节点与被测服务之间的网络延迟和带宽不会成为新的瓶颈。通常,Worker节点应尽可能靠近被测服务部署以减少网络干扰。
性能测试是一个“测试-分析-优化-再测试”的循环过程。Locust作为一个灵活、强大的工具,给了我们定义复杂场景和快速验证的能力。但更重要的是,要理解其数据背后的含义,结合全方位的系统监控,才能真正找到系统的瓶颈所在,推动性能优化。记住,工具只是手段,对系统行为和数据的洞察力才是核心。