4、压测工具选型:JMeter、wrk、kafka-producer-perf-test、自定义Go压测脚本的优缺点对比

做延迟压测,选对工具等于成功了一半。

我见过不少团队,一上来就拍脑袋选JMeter,结果压到一半发现线程数上不去,延迟数据还飘得厉害。也见过有人用wrk压HTTP接口,压得挺爽,但换到Kafka就傻眼了——wrk根本不支持。

所以这一章,咱们把市面上主流的几个压测工具拉出来遛遛。JMeter、wrk、kafka-producer-perf-test,还有我自己经常写的自定义Go脚本。每个工具我都会结合项目里的真实踩坑经历来讲,帮你少走弯路。

核心观点:没有完美的工具,只有最适合场景的选择。延迟压测尤其敏感,工具本身的调度开销、线程模型、统计精度,都会直接影响你的测试结论。

4.1 JMeter:功能全面,但小心它的“重量级”

JMeter是老牌压测工具了。图形化界面、插件生态丰富、支持各种协议——HTTP、TCP、JMS、Kafka(通过插件)。看起来什么都能干。

但说实话,我在做交易系统延迟压测时,很少用它。为什么?

  • 线程模型太重:JMeter每个虚拟用户就是一个Java线程。压到几百并发时,GC暂停就会引入额外的延迟抖动。你测的是系统延迟,还是JVM的GC延迟?
  • 统计精度不够:JMeter默认的聚合报告是秒级采样。对于微秒级的延迟波动,基本抓不住。我曾经用JMeter压一个内存队列,结果报告显示平均延迟0.1ms,但实际用Go脚本抓到的P99是0.8ms——差了8倍。
  • 分布式压测配置繁琐:想压到万级QPS?你得搭Master-Slave集群,网络同步、时间校准、结果聚合,每一步都是坑。

我的建议:JMeter适合做功能验证和低并发场景的基准测试。比如你刚搭好一个数据总线,想看看接口通不通、基本延迟在不在合理范围,用JMeter快速跑一轮没问题。但别用它做高精度延迟分析。

4.2 wrk:轻量级HTTP利器,但协议受限

wrk是我个人非常喜欢的工具。它基于Reactor模式,用C语言编写,单机就能压出几十万QPS。启动快、结果清晰、延迟统计到毫秒级。

它的优点很明显:

  • 性能极高:相比JMeter,wrk的线程模型非常轻量。它用少量线程配合epoll/kqueue处理海量连接,几乎不产生额外开销。
  • Lua脚本扩展:你可以写简单的Lua脚本来自定义请求内容、参数化、甚至做简单的响应校验。
  • 延迟分布一目了然:wrk默认输出延迟的分布情况,包括P50、P75、P90、P99。对于快速评估系统延迟特性非常方便。

但wrk的短板也很致命:

  • 只支持HTTP:你没法用它压Kafka、RabbitMQ、TCP私有协议。交易数据总线很多场景是二进制协议或消息队列,wrk完全无能为力。
  • 单机模式:wrk没有分布式能力。单机压到瓶颈后,你没法简单扩展。
  • 统计粒度粗:wrk的延迟统计是基于整个测试周期的。你没法看到时间序列上的延迟变化——比如某段时间突然抖动,wrk只会把它平均掉。

注意:我曾经用wrk压一个HTTP网关,结果发现P99延迟从2ms跳到了50ms。wrk报告里只显示一个平均值,我根本不知道抖动发生在哪一秒。后来换了Go脚本按秒输出延迟分布,才定位到是网关的定时任务触发了Full GC。

4.3 kafka-producer-perf-test:专为Kafka而生

这个工具是Kafka官方自带的压测脚本,位于$KAFKA_HOME/bin/目录下。它的定位非常明确:只压Kafka Producer的吞吐和延迟。

用法很简单:

bin/kafka-producer-perf-test.sh \
  --topic test-topic \
  --num-records 1000000 \
  --record-size 1024 \
  --throughput 10000 \
  --producer-props bootstrap.servers=localhost:9092

它的优势:

  • 原生支持Kafka:直接调用Kafka Producer API,不需要额外适配。压出来的结果就是生产环境的真实表现。
  • 延迟统计详细:它会输出发送延迟的分布,包括50th、75th、95th、99th百分位。而且支持按时间窗口输出,方便观察延迟波动。
  • 吞吐控制精准:你可以通过--throughput参数限制发送速率,模拟不同的负载场景。

但它的缺点也很明显:

  • 只适用于Kafka:你没法用它压别的中间件。
  • 单机模式:和wrk一样,没有分布式能力。想压集群极限?你得自己写脚本在多台机器上同时跑。
  • 定制能力弱:你没法在发送前后插入自定义逻辑——比如记录发送时间戳、模拟业务字段、做响应校验。

我的经验:kafka-producer-perf-test适合做Kafka集群的基准测试。我每次搭建新的Kafka集群,第一件事就是用这个工具跑一轮延迟基线。但如果你要压的是“数据总线”这种包含多个环节的链路,它就不够用了。

4.4 自定义Go压测脚本:灵活、精准、可控

说到这儿,你可能会问:那到底用什么工具做交易数据总线的延迟压测?

我的答案是:自定义Go脚本

为什么是Go?

  • 协程轻量:Go的goroutine比Java线程轻量得多。你可以轻松启动几万个并发协程,而不会引入明显的调度开销。
  • 延迟统计精准:你可以用time.Now()time.Since()精确到纳秒级。配合直方图库(比如github.com/HdrHistogram/hdrhistogram-go),能输出P50、P99、P999甚至P9999的延迟。
  • 完全可控:你想怎么压就怎么压。可以按时间窗口输出延迟分布,可以在请求前后插入日志,可以模拟业务逻辑,可以动态调整并发数。
  • 跨平台部署:编译成二进制文件,扔到服务器上就能跑。不需要装JRE、不需要配环境。

下面是我常用的一个压测脚本骨架:

package main

import (
    "fmt"
    "time"
    "sync"
    "github.com/HdrHistogram/hdrhistogram-go"
)

func main() {
    // 创建一个直方图,记录1微秒到1分钟的延迟
    hist := hdrhistogram.New(1, 60_000_000, 3)
    
    var wg sync.WaitGroup
    concurrency := 1000
    totalRequests := 100000
    
    start := time.Now()
    
    for i := 0; i < concurrency; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < totalRequests/concurrency; j++ {
                t1 := time.Now()
                // 这里放你的业务请求
                // e.g., sendKafkaMessage()
                elapsed := time.Since(t1)
                hist.RecordValue(elapsed.Microseconds())
            }
        }()
    }
    
    wg.Wait()
    elapsed := time.Since(start)
    
    fmt.Printf("总耗时: %v\n", elapsed)
    fmt.Printf("P50: %v\n", hist.ValueAtPercentile(50))
    fmt.Printf("P99: %v\n", hist.ValueAtPercentile(99))
    fmt.Printf("P999: %v\n", hist.ValueAtPercentile(99.9))
}

当然,自定义脚本也有代价:

  • 开发成本:你得自己写代码、处理并发、做统计。不像JMeter那样开箱即用。
  • 需要维护:压测脚本也是代码,需要版本管理、测试、文档。
  • 协议适配:如果数据总线用的是私有协议,你得自己实现客户端。

我的建议:对于交易数据总线这种对延迟极度敏感的系统,花两天时间写一个Go压测脚本,远比花两天时间调JMeter参数要值得。你想想看,压测工具本身的性能开销如果占了总延迟的10%,那你的测试结论还有什么意义?

4.5 工具对比总表

为了方便你快速决策,我把四个工具的核心维度整理成了表格:

维度 JMeter wrk kafka-producer-perf-test 自定义Go脚本
协议支持 HTTP、TCP、JMS、Kafka等 仅HTTP 仅Kafka Producer 任意(需自行实现)
并发模型 Java线程(重量级) Reactor(轻量级) Java线程(中等) Go协程(极轻量)
延迟统计精度 秒级(较粗) 毫秒级(中等) 毫秒级(中等) 纳秒级(精准)
分布式能力 支持(配置复杂) 不支持 不支持 需自行实现
学习成本 低(图形化) 低(命令行) 低(命令行) 高(需编程)
适用场景 功能验证、低并发基准 HTTP接口快速压测 Kafka集群基准测试 高精度延迟压测、复杂链路

4.6 我的选型建议

说了这么多,到底怎么选?我总结三条原则:

  1. 快速验证用wrk:如果你只是压HTTP接口,想看看基本延迟水平,wrk是最快的选择。一条命令搞定。
  2. Kafka基准用官方工具:kafka-producer-perf-test是Kafka团队自己写的,压出来的结果最贴近生产。别自己造轮子。
  3. 高精度延迟压测用Go脚本:交易数据总线的延迟要求通常是微秒级到毫秒级。这时候工具本身的性能开销必须足够低。Go脚本是唯一能让你精确控制每个环节的选择。

避坑指南:我曾经用JMeter压一个延迟要求<1ms的数据总线,结果JMeter自己就占了0.3ms的调度开销。测出来的延迟是0.8ms,实际系统延迟只有0.5ms。这个误差直接导致我们误判了系统性能,多花了两周做优化。后来换成Go脚本,才看到真实数据。

嗯,工具选型就聊到这儿。下一章咱们会深入讲压测环境的搭建和校准——你会发现,环境本身对延迟的影响,可能比工具还大。


专注资料整理