压测工具选型与实战:JMeter/Locust/wrk 对比、脚本编写、参数化与关联、分布式压测部署
做交易系统压测这么多年,我经常被问到同一个问题:「到底该用哪个压测工具?」
说实话,没有银弹。每个工具都有自己的脾气。我见过有人用JMeter压出了天大的坑,也见过有人用wrk写出了漂亮的脚本。关键是你得知道它们各自擅长什么。
三大工具横向对比
先看一张我整理的对比表,这样你心里有个底:
| 维度 | JMeter | Locust | wrk |
|---|---|---|---|
| 协议支持 | HTTP/HTTPS/WebSocket/JMS/自定义 | HTTP/HTTPS(可扩展) | HTTP/HTTPS |
| 脚本语言 | GUI配置 + Groovy/Java | Python | Lua |
| 并发模型 | 线程池 | 协程(gevent) | 多线程 + epoll |
| 分布式支持 | 原生支持(Master-Slave) | 原生支持(Master-Worker) | 需自行搭建 |
| 学习曲线 | 中等(GUI友好) | 低(Python基础即可) | 高(需懂Lua) |
| 适合场景 | 复杂业务逻辑、多协议 | 快速原型、灵活扩展 | 高并发纯压力测试 |
嗯,这里要注意。这张表只是参考,实际选型还得看你的具体场景。
JMeter实战:交易系统压测脚本编写
我个人习惯用JMeter做交易系统的压测,原因很简单——它支持自定义协议。交易系统里经常有二进制协议、FIX协议,JMeter的Java Sampler可以搞定。
先看一个最简单的HTTP请求脚本:
// 在JMeter的JSR223 Sampler中编写
import org.apache.jmeter.samplers.SampleResult;
String orderId = vars.get("orderId");
String payload = "{ \"orderId\": \"" + orderId + "\", \"side\": \"BUY\", \"qty\": 100 }";
SampleResult result = new SampleResult();
result.sampleStart();
// 发送请求
def response = httpClient.post("https://api.trade.com/order", payload);
result.setResponseData(response.getBytes());
result.sampleEnd();
result.setSuccessful(response.getStatusCode() == 200);
return result;
参数化与关联:让脚本活起来
说白了,参数化就是让每次请求都不一样。关联就是让后面的请求依赖前面的响应。
举个例子,交易系统里最常见的场景:先登录获取token,再用token下单。
// 第一步:登录,提取token
// 使用JSON Extractor
// Variable Name: authToken
// JSON Path: $.data.token
// 第二步:下单,使用token
// 在HTTP Header Manager中设置
// Name: Authorization
// Value: Bearer ${authToken}
为什么会这样?因为交易系统的鉴权机制决定了你必须先拿到token。我建议你在写脚本前,先画一个请求依赖图,这样参数化和关联的逻辑就清晰了。
Locust实战:Python写压测脚本
如果你团队里Pythoner多,Locust是个好选择。它的协程模型在IO密集型场景下表现不错。
from locust import HttpUser, task, between
class TradeUser(HttpUser):
wait_time = between(0.5, 2.0)
def on_start(self):
# 登录获取token
resp = self.client.post("/login", json={
"username": "test_user",
"password": "test_pass"
})
self.token = resp.json()["token"]
@task(3)
def place_order(self):
self.client.post("/order",
json={"symbol": "BTC/USDT", "side": "BUY", "qty": 0.01},
headers={"Authorization": f"Bearer {self.token}"}
)
@task(1)
def cancel_order(self):
# 先获取订单列表
orders = self.client.get("/orders",
headers={"Authorization": f"Bearer {self.token}"}
).json()
if orders:
order_id = orders[0]["orderId"]
self.client.delete(f"/order/{order_id}",
headers={"Authorization": f"Bearer {self.token}"}
)
wrk实战:极致性能压测
wrk是我压测「纯性能」时的首选。它用C写的,性能极高。但缺点也很明显——Lua脚本写起来不太友好。
-- wrk的Lua脚本示例
wrk.method = "POST"
wrk.body = '{"symbol":"BTC/USDT","side":"BUY","qty":0.01}'
wrk.headers["Content-Type"] = "application/json"
-- 每个请求前执行
request = function()
-- 生成随机订单ID
local orderId = math.random(1000000, 9999999)
wrk.body = '{"orderId":"' .. orderId .. '","symbol":"BTC/USDT","side":"BUY","qty":0.01}'
return wrk.format()
end
-- 响应处理
response = function(status, headers, body)
if status ~= 200 then
-- 记录错误
print("Error: " .. status)
end
end
运行命令:
wrk -t12 -c400 -d30s -s trade.lua https://api.trade.com/order
这个命令的意思是:12个线程,400个并发连接,持续30秒。你想想看,这个量级对大多数交易系统来说已经够用了。
分布式压测部署
单机压测有个瓶颈——你的压测机本身就成了瓶颈。我记得有一次压测,单机只能产生5000 QPS,但目标系统能扛2万。怎么办?上分布式。
JMeter分布式部署:
# 1. 启动Master节点
jmeter -n -t test_plan.jmx -R slave1:1099,slave2:1099 -l results.jtl
# 2. 在每个Slave节点启动Agent
jmeter-server -Dserver.rmi.localport=1099
Locust分布式部署:
# 启动Master
locust -f trade_test.py --master --host=https://api.trade.com
# 启动Worker(可以开多个终端)
locust -f trade_test.py --worker --master-host=192.168.1.100
我的选型建议
说了这么多,到底怎么选?我总结一下:
- 交易系统功能压测(比如测试下单、撤单、查询等业务流程):用JMeter。它的断言、监听器、参数化功能最完善。
- 快速验证性能瓶颈(比如想知道某个接口能扛多少QPS):用wrk。写个Lua脚本,几分钟就能出结果。
- 持续集成中的性能测试(比如每次代码提交后自动跑压测):用Locust。Python脚本容易集成到CI/CD流水线。
嗯,最后说一句。工具只是工具,真正重要的是你对交易系统业务的理解。你想想看,如果你不知道订单的流转路径,不知道数据库的锁机制,再好的压测工具也测不出真正的瓶颈。
这张图是我自己画的决策流程。你从需求出发,一步步往下走,基本不会选错。