Locust是一个用Python编写的开源负载测试工具,它允许使用Python代码定义用户行为,能够模拟数百万用户并发来测试您的系统。
Locust的优势:使用纯 Python 编写测试场景、分布式支持轻松扩展到多台机器、实时Web UI更直观监控测试进度和结果、高并发能力是基于gevent的协程实现
1.安装与配置
基础安装
bash
# 使用 pip 安装
pip install locust
# 验证安装
locust --version
选择依赖项
bash
# 安装带额外功能的版本
pip install locust[extra]
# 或单独安装所需组件
pip install pyzmq # 分布式模式支持
pip install geventhttpclient # 更快的 HTTP 客户端
2. 脚本编写
python
from locust import HttpUser, TaskSet, task, between
import random
class UserBehavior(TaskSet):
@task(3) # 权重为3,执行频率更高
def view_products(self):
# 查看产品列表
self.client.get("/api/products")
@task(2)
def view_product_detail(self):
# 查看产品详情,使用随机ID
product_id = random.randint(1, 100)
self.client.get(f"/api/products/{product_id}")
@task(1)
def create_order(self):
# 创建订单
headers = {'Content-Type': 'application/json'}
data = {
"product_id": random.randint(1, 100),
"quantity": random.randint(1, 5)
}
self.client.post("/api/orders", json=data, headers=headers)
class WebsiteUser(HttpUser):
tasks = [UserBehavior]
wait_time = between(1, 5) # 用户等待时间1-5秒
def on_start(self):
# 用户启动时执行(如登录)
self.client.post("/api/login", json={
"username": "test_user",
"password": "test_pass"
})
3. 功能配置
自定义客户端配置
python
from locust import HttpUser, task
import json
class ApiUser(HttpUser):
host = "https://api.zmtests.com"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.client.verify = False # 禁用SSL验证
self.client.timeout = 30 # 设置超时时间
@task
def complex_request(self):
# 自定义请求头
headers = {
"Authorization": "Bearer token123",
"User-Agent": "LocustLoadTest/1.0"
}
# 带参数的GET请求
params = {"page": 1, "limit": 20}
response = self.client.get("/api/data", params=params, headers=headers)
# 处理响应
if response.status_code == 200:
data = response.json()
print(f"Received {len(data)} items")
事件钩子编写
python
from locust import events
from locust.runners import MasterRunner, WorkerRunner
import time
@events.test_start.add_listener
def on_test_start(environment, **kwargs):
print("测试开始执行")
if isinstance(environment.runner, MasterRunner):
print("这是Master节点")
elif isinstance(environment.runner, WorkerRunner):
print("这是Worker节点")
@events.request.add_listener
def on_request(request_type, name, response_time, response_length,
response, context, exception, **kwargs):
if exception:
print(f"请求失败: {name}, 错误: {exception}")
else:
print(f"请求成功: {name}, 响应时间: {response_time}ms")
@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
print("测试结束")
4. 运行
启动方式
Web UI 模式
bash
# 基础启动
locust -f locustfile.py
# 指定主机和端口
locust -f locustfile.py --host=https://api.zmtests.com --web-port=8080
# 无Web界面模式(用于CI/CD)
locust -f locustfile.py --headless -u 100 -r 10 -t 10m
命令行参数
bash
locust -f locustfile.py \
--host=https://api.zmtests.com \
--users=1000 \ # 最大用户数
--spawn-rate=10 \ # 每秒启动用户数
--run-time=30m \ # 运行时间
--web-host=0.0.0.0 \ # Web界面绑定地址
--web-port=8080 \ # Web界面端口
--headless \ # 无头模式
--csv=results \ # 导出CSV结果
--html=report.html # 生成HTML报告
分布式运行
bash
# 启动Master节点
locust -f locustfile.py --master --expect-workers=4
# 启动Worker节点(在其他机器上)
locust -f locustfile.py --worker --master-host=192.168.1.100

5. 性能优化
优化测试脚本
python
from locust import HttpUser, task, events
import gevent
from gevent.pool import Group
class OptimizedUser(HttpUser):
@task
def parallel_requests(self):
# 并行执行多个请求
urls = [
"/api/products",
"/api/categories",
"/api/users/me"
]
group = Group()
for url in urls:
group.spawn(lambda u=url: self.client.get(u))
group.join()
@task
def batch_operations(self):
# 批量操作减少请求次数
orders = [
{"product_id": i, "quantity": 1}
for i in range(1, 6)
]
self.client.post("/api/orders/batch", json=orders)
自定义客户端
python
from locust import HttpUser
from locust.clients import HttpSession
import urllib3
class CustomHttpUser(HttpUser):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 禁用不安全请求警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
@task
def custom_request(self):
# 使用连接池和会话复用
with self.client.get("/api/data", catch_response=True) as response:
if response.elapsed.total_seconds() > 2:
response.failure("响应时间过长")
elif response.status_code != 200:
response.failure(f"HTTP错误: {response.status_code}")
else:
response.success()
6. 数据驱动测试
python
import csv
import json
from locust import HttpUser, task
class DataDrivenUser(HttpUser):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.test_data = self.load_test_data()
def load_test_data(self):
# 从CSV文件加载测试数据
data = []
with open('test_data.csv', 'r') as f:
reader = csv.DictReader(f)
for row in reader:
data.append(row)
return data
@task
def create_user(self):
if self.test_data:
user_data = random.choice(self.test_data)
self.client.post("/api/users", json=user_data)
7. 验证
python
from locust import HttpUser, task
import jsonschema
from jsonschema import validate
class ValidatingUser(HttpUser):
user_schema = {
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
"email": {"type": "string", "format": "email"}
},
"required": ["id", "name", "email"]
}
@task
def validate_response(self):
with self.client.get("/api/user/1", catch_response=True) as response:
try:
data = response.json()
validate(instance=data, schema=self.user_schema)
# 自定义业务逻辑验证
if data["name"] and len(data["name"]) > 0:
response.success()
else:
response.failure("用户名无效")
except jsonschema.ValidationError as e:
response.failure(f"响应格式错误: {e}")
except ValueError:
response.failure("响应不是有效的JSON")
8. 集成与自动化
bash
FROM python:3.9
RUN pip install locust
COPY locustfile.py /mnt/locustfile.py
WORKDIR /mnt
EXPOSE 8089
CMD ["locust", "-f", "locustfile.py", "--host", "http://target-service"]
CI/CD 集成
bash
#yaml
name: Load Test
on: [push]
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: pip install locust
- name: Run load test
run: |
locust -f locustfile.py \
--headless \
--users 100 \
--spawn-rate 10 \
--run-time 5m \
--host ${{ secrets.TARGET_HOST }} \
--html report.html \
--only-summary