Kafka JMH 微基准测试模块完全指南:运行、剖析与编写正确的高性能基准
发布时间:2026/9/10 11:12:37 锦皓数字建站

Kafka JMH 微基准测试模块完全指南运行、剖析与编写正确的高性能基准【免费下载链接】KafkaApache Kafka - A distributed event streaming platform项目地址: https://gitcode.com/GitHub_Trending/kafka4/kafka导读本文以 Apache Kafka 仓库中的 jmh-benchmarks/README.md 为核心系统讲解如何在 Kafka 项目中利用 OpenJDK JMH 框架编写和运行微基准测试micro-benchmark。你将掌握jmh.sh脚本的完整用法、async profiler 与 GC profiler 的正确接入方式、脱离 Gradle 直接运行基准 Jar 的方法以及基于源码级示例如LRUCacheBenchmark、RecordBatchIterationBenchmark编写严谨基准的实战技巧。一、为什么 Kafka 需要 JMH 基准测试模块在 JVM 上编写正确的微基准测试非常困难存在大量因编译器优化而导致的隐蔽陷阱——死代码消除、常量折叠、循环展开都可能让测量结果失真。JMHJava Microbenchmark Harness是 OpenJDK 推出的专用于运行和分析 Java或其他 JVM 语言微基准与宏基准的框架它通过 fork 独立 JVM、预热warmup、黑洞消费Blackhole等机制帮助开发者获得可信的性能数据。Kafka 在仓库中专门维护了jmh-benchmarks模块该模块在根目录的 settings.gradle 中被纳入 Gradle 多模块构建并在根目录的 build.gradle 中声明使用com.gradleup.shadow插件来组装可执行的 uber-jar。模块内目前包含约 60 个基准类覆盖了 Kafka 的核心热点路径例如recordRecordBatchIterationBenchmark记录批次迭代、CompressedRecordBatchValidationBenchmark、UncompressedRecordBatchValidationBenchmarkproducerProducerRequestBenchmark、RecordAccumulatorReadyBenchmark、RecordAccumulatorFlushBenchmarkcommonFetchRequestBenchmark、FetchResponseBenchmark、MetadataResponseBenchmarkpartition / fetcherPartitionMakeFollowerBenchmark、ReplicaFetcherThreadBenchmarkstreams / connect / metadata / storageStreamsStickyAssignorBenchmark、JsonConverterBenchmark、KRaftMetadataRequestBenchmark、ProducerStateManagerBench等二、运行基准测试jmh.sh 快速上手直接在 Gradle 任务中传递 JMH 参数既繁琐又易错因此模块提供了现成的脚本 jmh-benchmarks/jmh.sh。该脚本先执行gradlew :jmh-benchmarks:clean :jmh-benchmarks:shadowJar构建 uber-jar随后通过java -jar直接启动 JMH 运行器并把所有剩余参数原样透传。脚本默认行为是运行全部基准./jmh-benchmarks/jmh.sh按名称或模式筛选基准传入一个模式或类名即可只运行匹配的基准例如只跑 LRU 缓存基准./jmh-benchmarks/jmh.sh LRUCacheBenchmark先列出哪些基准匹配给定模式而不实际执行-l是 JMH 的 list 参数./jmh-benchmarks/jmh.sh -l LRUCacheBenchmark覆盖 fork、迭代与预热参数运行指定测试并将 fork 次数、测量迭代数和预热迭代数全部覆盖为2./jmh-benchmarks/jmh.sh -f 2 -i 2 -wi 2 LRUCacheBenchmark结合 profiler 运行在 Linux 上同时启用 GC profiler 与 async profiler 并输出火焰图分号需要转义避免被 Shell 当作命令分隔符./jmh-benchmarks/jmh.sh -prof gc -prof async:libPath/path/to/libasyncProfiler.so\;outputflamegraph LRUCacheBenchmark三、用 async profiler 验证基准确实在测“该测的东西”对微基准而言检查 profiler 输出是一种良好实践它可以验证基准是否代表了预期的应用行为、是否真的在测量目标代码。常见的反例包括在基准中误用了昂贵的 mock或者把测试初始化代码意外包含进了被测量代码。JMH 内置了对 async-profiler 的集成Linux 下指定.so库路径即可./jmh-benchmarks/jmh.sh -prof async:libPath/path/to/libasyncProfiler.so输出火焰图outputflamegraph参数中的分号已转义防止被 Shell 解释为命令分隔符./jmh-benchmarks/jmh.sh -prof async:libPath/path/to/libasyncProfiler.so\;outputflamegraphasync-profiler 2.0 支持同时进行 CPU、内存分配alloc与锁lock剖析并输出 JFR 格式同样注意分号转义./jmh-benchmarks/jmh.sh -prof async:libPath/path/to/libasyncProfiler.so\;outputjfr\;alloc\;lock LRUCacheBenchmarkasync profiler 有大量可配置参数查看完整参数说明./jmh-benchmarks/jmh.sh -prof async:help四、用 GC profiler 衡量分配率运行基准时建议加上-prof gc来测量其内存分配速率./jmh-benchmarks/jmh.sh -prof gc其中尤其值得关注norm类分配率指标——它统计的是每次操作per operation的分配量而非每秒分配量。后者在代码变快时反而可能上升造成“越快越差”的假象norm指标可以规避这一误导。五、脱离 Gradle 直接运行基准构建完成后基准 Jar 位于jmh-benchmarks/build/libs/下命名形如kafka-jmh-benchmarks-*.jar因此完全可以像运行任何可执行 Jar 一样脱离 Gradle 执行例如java -jar kafka-repo-dir/jmh-benchmarks/build/libs/kafka-jmh-benchmarks-*.jar -f2 LRUCacheBenchmark这一能力正是 jmh.sh 底层的执行方式也便于在 CI 或性能回归脚本中复用。六、Gradle 任务与 Shadow Jar 机制JMH 通常期望以独立的 Maven 项目形态存在。jmh-benchmarks模块则通过 Gradle Shadow Jar 插件模拟这一行为把基准代码与所需的 JMH 运行时类合并组装为单个 uber-jar。模块提供两个核心 Gradle 任务默认在 Kafka 仓库根目录用./gradlew执行任务作用jmh-benchmarks:shadowJar创建运行基准所需的 uber jarjmh-benchmarks:jmh依次执行clean与shadowJar然后运行全部基准如果没有显式指定基准模式JMH 采用默认模式throughput吞吐量。此外根目录 build.gradle 中针对 shadowJar 做了专门的 archiveClassifier 配置避免与原始 jar 互相覆盖并保证了kafka-jmh-benchmarks-*.jar的命名形态。七、常用 JMH 选项速查以下为模块文档列出的常用 JMH 命令行选项-e regexp Benchmarks to exclude from the run. -f int How many times to fork a single benchmark. Use 0 to disable forking altogether. Warning: disabling forking may have detrimental impact on benchmark and infrastructure reliability, you might want to use different warmup mode instead. -i int Number of measurement iterations to do. Measurement iterations are counted towards the benchmark score. (default: 1 for SingleShotTime, and 5 for all other modes) -l List the benchmarks that match a filter, and exit. -lprof List profilers, and exit. -o filename Redirect human-readable output to a given file. -prof profiler Use profilers to collect additional benchmark data. Some profilers are not available on all JVMs and/or all OSes. Please see the list of available profilers with -lprof. -v mode Verbosity mode. Available modes are: [SILENT, NORMAL, EXTRA] -wi int Number of warmup iterations to do. Warmup iterations are not counted towards the benchmark score. (default: 0 for SingleShotTime, and 5 for all other modes)查看全部选项可运行 JMH 时加-h标志。八、编写基准测试源码级范例拆解8.1 最简单的入门范例LRUCacheBenchmarkLRUCacheBenchmark.java 是模块内最简单的基准之一完整展示了 JMH 的核心骨架State(Scope.Thread)状态对象仅对单个线程可见线程私有OutputTimeUnit(TimeUnit.MILLISECONDS)结果以毫秒为单位输出Setup(Level.Trial)在整轮基准开始前初始化 10,000 个不同的 key/value 以及容量为 100 的LRUCacheBenchmark方法testCachePerformance()通过自增 counter 轮换使用不同 key 执行put与get其返回值被 JMH 自动消费防止死代码消除main()方法内使用OptionsBuilder声明.include(LRUCacheBenchmark.class.getSimpleName())与.forks(2)后交给Runner执行——这是 JMH 标准编程式启动入口。State(Scope.Thread) OutputTimeUnit(TimeUnit.MILLISECONDS) public class LRUCacheBenchmark { private static final int DISTINCT_KEYS 10_000; private LRUCacheString, String lruCache; private long counter 0; Setup(Level.Trial) public void setUp() { // 预生成 DISTINCT_KEYS 个 key/value创建容量 100 的 LRUCache lruCache new LRUCache(100); } Benchmark public String testCachePerformance() { counter; int index (int) (counter % DISTINCT_KEYS); String hashkey keys[index]; lruCache.put(hashkey, values[index]); return lruCache.get(hashkey); } public static void main(String[] args) throws RunnerException { Options opt new OptionsBuilder() .include(LRUCacheBenchmark.class.getSimpleName()) .forks(2) .build(); new Runner(opt).run(); } }8.2 进阶注解组合RecordBatchIterationBenchmarkRecordBatchIterationBenchmark.java 展示了更完整的生产级注解用法是理解“如何把基准写严谨”的绝佳样本State(Scope.Benchmark)状态在所有线程间共享Fork(value 1)仅 fork 一次Warmup(iterations 5)、Measurement(iterations 15)5 轮预热、15 轮正式测量且通过Param(value {LZ4, SNAPPY, GZIP, ZSTD, NONE})对五种压缩类型进行参数化扫描OperationsPerInvocation(value batchCount)声明单次调用内实际处理了batchCount个批次保证 ops/s 统计口径准确Fork(jvmArgsAppend -Xmx8g)为特定基准单独放大堆内存基准方法内部使用Blackhole.consume(...)消费每条记录配合 try-with-resources 关闭迭代器避免测试初始化代码与资源泄漏污染测量结果。8.3 消除编译器优化干扰ByteUtilsBenchmarkByteUtilsBenchmark.java 用于对比 Kafka 中 VarInt/VarLong 编解码的多种实现legacy、Protobuf 风格、Netty 风格、unrolled 展开写法其中值得借鉴的关键技巧包括CompilerControl(CompilerControl.Mode.DONT_INLINE)禁止 JIT 将被测方法内联确保测量到真实方法体State(Scope.Benchmark)嵌套类 Setup(Level.Invocation)每次调用前重建 ByteBuffer避免迭代间状态残留使用固定种子random.setSeed(133713371337L)的SecureRandom生成可复现的随机数据集并通过Param({1, 3, 5, 7})控制不同字节长度的取值分布——其注释还说明 Kafka 实际场景中大多数整数只有 12 字节。8.4 基准中的真实环境初始化BenchmarkConfigUtils需要启动真实组件如 GroupCoordinator、存储层的基准可复用 BenchmarkConfigUtils.java 的createDummyBrokerConfig()它以Properties形式一次性配置 KRaft 角色process.rolesbroker,controller、监听器、日志目录、副本因子、offset topic 分区数、网络/后台线程数等完整 broker 参数为基准构造接近真实部署的测试环境。九、结语jmh-benchmarks模块是 Kafka 性能工程体系的重要组成部分jmh.sh屏蔽了 Gradle 参数透传的繁琐-prof async与-prof gc帮助开发者验证测量目标与分配行为而模块内近 60 个覆盖 producer、consumer、record、metadata、streams 等核心路径的基准类既是对热点代码的性能守护也是学习正确 JMH 写法的现成教材。当你需要为 Kafka 提交一个性能敏感的改动时善用该模块进行 fork 隔离、预热与分配率验证能让每一个性能结论都建立在可复现、可解释的测量之上。【免费下载链接】KafkaApache Kafka - A distributed event streaming platform项目地址: https://gitcode.com/GitHub_Trending/kafka4/kafka创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。