压测工具选型与实战: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;
我的经验:交易系统的压测脚本,一定要把「订单ID」参数化。我曾经见过有人用固定订单ID压测,结果系统以为是重复订单,全部拒绝。白白浪费了3个小时。

参数化与关联:让脚本活起来

说白了,参数化就是让每次请求都不一样。关联就是让后面的请求依赖前面的响应。

举个例子,交易系统里最常见的场景:先登录获取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}"}
            )
避坑指南:我曾经在Locust脚本里忘了加wait_time,结果压测机直接把交易系统打挂了。记住,真实用户不会毫秒级连续点击,一定要模拟合理的思考时间。

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
核心要点:分布式压测时,Master节点只负责调度和汇总,真正的压力由Worker节点产生。我建议Worker节点和Master节点在同一个内网,避免网络延迟影响压测结果。

我的选型建议

说了这么多,到底怎么选?我总结一下:

  • 交易系统功能压测(比如测试下单、撤单、查询等业务流程):用JMeter。它的断言、监听器、参数化功能最完善。
  • 快速验证性能瓶颈(比如想知道某个接口能扛多少QPS):用wrk。写个Lua脚本,几分钟就能出结果。
  • 持续集成中的性能测试(比如每次代码提交后自动跑压测):用Locust。Python脚本容易集成到CI/CD流水线。

嗯,最后说一句。工具只是工具,真正重要的是你对交易系统业务的理解。你想想看,如果你不知道订单的流转路径,不知道数据库的锁机制,再好的压测工具也测不出真正的瓶颈。

我的习惯:每次压测前,我都会先画一张「请求-响应」流程图,标注出参数化点和关联点。这张图比任何工具都重要。
压测工具选型决策流程 压测需求分析 需要测试复杂业务流程? (多协议、断言、监听器) JMeter GUI配置 + Java/Groovy脚本 需要极致性能? (纯压力测试) wrk C语言 + Lua脚本 Locust Python协程 + 灵活扩展

这张图是我自己画的决策流程。你从需求出发,一步步往下走,基本不会选错。

专注资料整理