3、测试工具链:wrk2、h2load、locust、自定义C++测试客户端、Python asyncio压测脚本

聊到性能测试,很多人的第一反应是「找个工具,跑一下」。嗯,这话没错,但跑出来的数字到底能不能信,就看你选什么工具、怎么用了。我见过不少团队,拿个通用压测工具随便压一压,然后对着 QPS 数字拍脑袋做容量规划,结果上线第一天就崩了。

说白了,量化交易系统的压测,跟 Web 压测完全是两码事。我们的核心链路是订单处理、行情推送、风控校验,延迟敏感度是微秒级的。你拿个测 HTTP 的工具去压 TCP 直连的行情网关,那数据基本是废的。

今天我就把这几年用过的、踩过坑的几款工具,挨个给你捋一遍。哪些场景该用谁,怎么配参数才能拿到真实数据,我都会说清楚。

3.1 wrk2:HTTP 压测的「老黄牛」

wrk2 是 wrk 的增强版,最大的改进是支持稳定的请求到达率。什么意思?就是它能保证每秒发出去的请求数量是恒定的,而不是「尽力而为」地猛发。这一点对延迟测试特别重要。

适用场景:REST API 接口、WebSocket 网关的 HTTP 升级握手。

我常用的命令模板

wrk2 -t4 -c100 -d30s -R2000 --latency http://192.168.1.100:8080/api/order
  • -t4:4 个线程
  • -c100:100 个并发连接
  • -d30s:持续 30 秒
  • -R2000:每秒发 2000 个请求
  • --latency:输出延迟分布
我的经验-R 参数别设太高,先按系统预估 QPS 的 80% 起步。我曾经一上来就设 5000,结果服务端直接拒绝连接,我还以为是工具坏了。

3.2 h2load:HTTP/2 和多路复用的利器

如果你的交易网关支持 HTTP/2(现在很多新一代系统都支持了),那 h2load 是首选。它原生支持多路复用,能模拟多个 Stream 在一条连接上并发请求。

为什么重要? 因为 HTTP/2 的多路复用特性,能大幅减少连接建立的开销。你用 wrk2 压 HTTP/1.1 得出的延迟,跟 h2load 压 HTTP/2 的结果,可能差一个数量级。

h2load -n100000 -c100 -m10 https://192.168.1.100:443/api/order
  • -n100000:总共发 10 万请求
  • -c100:100 个并发连接
  • -m10:每个连接最多 10 个并发 Stream
注意:h2load 默认走 TLS,如果你的服务没配证书,记得加 --no-tls 参数。我有个同事因为这个参数没加,折腾了一下午。

3.3 locust:Python 生态的「瑞士军刀」

Locust 的优势在于灵活。你可以用 Python 写任意复杂的用户行为脚本,比如先登录、再查询、再下单、再撤单。这种场景化的压测,wrk2 和 h2load 都做不到。

我一般用它来压测什么?

  • 模拟多个交易员同时操作
  • 测试风控规则的并发校验
  • 验证数据库连接池在压力下的表现

一个简单的 locustfile.py 示例:

from locust import HttpUser, task, between

class TraderUser(HttpUser):
    wait_time = between(0.1, 0.5)

    @task
    def place_order(self):
        self.client.post("/api/order", json={
            "symbol": "BTCUSDT",
            "side": "buy",
            "price": 50000,
            "quantity": 0.1
        })
避坑指南:Locust 的 master-slave 模式在分布式压测时很好用,但要注意网络延迟。我曾经在跨机房部署时,因为 master 和 slave 之间的时钟不同步,导致聚合数据完全对不上。

3.4 自定义 C++ 测试客户端:微秒级延迟的终极方案

说实话,前面几个工具在大多数场景下够用了。但如果你要压测的是低延迟行情网关、FPGA 加速卡、或者内存撮合引擎,那它们的精度就不够了。为什么?因为 wrk2 和 Locust 的计时精度在毫秒级,而我们的系统延迟是微秒级的。

这时候,就得自己写 C++ 客户端了。

我一般用 libuvboost.asio 做事件循环,配合 clock_gettime 获取纳秒级时间戳。核心逻辑很简单:

#include <uv.h>
#include <chrono>

void on_write(uv_write_t* req, int status) {
    auto end = std::chrono::high_resolution_clock::now();
    auto start = *(std::chrono::high_resolution_clock::time_point*)req->data;
    auto latency = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
    // 记录延迟
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    uv_tcp_t *socket = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, socket);
    // ... 连接、发送、接收逻辑
    uv_run(loop, UV_RUN_DEFAULT);
    return 0;
}
关键点:一定要用 high_resolution_clock,并且要在发送前和收到响应后各取一次时间戳。中间不能有任何阻塞操作,否则测出来的延迟是假的。

3.5 Python asyncio 压测脚本:快速原型验证

有时候你只是想快速验证一个想法,比如「这个接口在 1000 并发下会不会超时」。这时候写 C++ 客户端就太重了。Python asyncio 配合 aiohttpwebsockets,几分钟就能写一个可用的压测脚本。

import asyncio
import aiohttp
import time

async def send_order(session, sem):
    async with sem:
        start = time.perf_counter()
        async with session.post("http://192.168.1.100:8080/api/order",
                                json={"symbol": "BTCUSDT", "price": 50000}) as resp:
            end = time.perf_counter()
            latency = (end - start) * 1000  # 毫秒
            return latency

async def main():
    sem = asyncio.Semaphore(100)  # 控制并发数
    async with aiohttp.ClientSession() as session:
        tasks = [send_order(session, sem) for _ in range(10000)]
        latencies = await asyncio.gather(*tasks)
        print(f"平均延迟: {sum(latencies)/len(latencies):.2f}ms")

asyncio.run(main())
注意:Python 的 GIL 会导致多线程压测不准,但 asyncio 是单线程事件循环,反而能更真实地模拟 IO 密集型场景。不过,如果你要压 CPU 密集型的业务逻辑,Python 就不太合适了。

3.6 工具选型对比

工具 适用场景 延迟精度 学习成本 我的推荐指数
wrk2 HTTP/1.1 REST API 毫秒级 ⭐⭐⭐⭐
h2load HTTP/2 多路复用 毫秒级 ⭐⭐⭐⭐
locust 复杂业务场景 毫秒级 ⭐⭐⭐
C++ 客户端 低延迟核心链路 微秒级 ⭐⭐⭐⭐⭐
Python asyncio 快速原型验证 毫秒级 ⭐⭐⭐

3.7 工具链选择的核心逻辑

说了这么多,你可能会问:「那我到底该用哪个?」

我的建议是:不要只依赖一个工具。一个成熟的量化交易团队,应该同时维护 2-3 套压测工具链。

  • 日常回归测试:用 wrk2 或 h2load,跑得快,结果稳定
  • 复杂场景模拟:用 locust,写脚本灵活
  • 核心链路压测:必须上自定义 C++ 客户端,精度才有保障
  • 快速验证:Python asyncio 脚本,改起来快

下面这张图,是我自己总结的工具链选择流程,你可以参考一下:

压测工具链选择流程 开始压测 延迟要求 < 1ms? C++ 客户端 wrk2 / h2load 场景复杂? Locust Python asyncio 微秒级延迟报告

嗯,工具链这块就先聊到这儿。记住一句话:没有最好的工具,只有最合适的场景。下一节我们会深入聊聊如何设计压测场景和参数,让这些工具真正发挥价值。


专注资料整理