使用 JMH 对 Spring Boot 应用程序进行基准测试

Posted

技术标签:

【中文标题】使用 JMH 对 Spring Boot 应用程序进行基准测试【英文标题】:Benchmarking spring boot application with JMH 【发布时间】:2017-05-17 06:00:55 【问题描述】:

我有一个spring boot 应用程序,我想使用JMH 对其进行基准测试。此集成的任何参考都会很有用。

【问题讨论】:

恕我直言 jmh 不适用于基准测试应用程序,但仅适用于特定方法。 我设法为 Spring MVC 应用程序做基准测试。他们应该是为 Spring-boot 应用程序做的方法 spring-cloud-sleuth benchmark samples 【参考方案1】:

解决方案比我想象的要简单。重要的部分是在初始化基准测试时启动 spring-boot 应用程序。为配置上下文定义一个类级别变量,并在设置基准测试期间对其进行引用。调用基准测试中的 bean 方法。

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;

@BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MINUTES)
@State(Scope.Thread)
public class ProcessFeedBenchMark   

    public static void main(String args[]) throws Exception 
        URLClassLoader classLoader = (URLClassLoader) ProcessFeedBenchMark.class.getClassLoader();
        StringBuilder classpath = new StringBuilder();
        for(URL url : classLoader.getURLs())
            classpath.append(url.getPath()).append(File.pathSeparator);
        classpath.append("/D:/work/zymespace/benchmark/src/main/resources/").append(File.pathSeparator);
        System.out.print(classpath.toString());
        System.setProperty("java.class.path", classpath.toString());

        Options opt = new OptionsBuilder()
        .include(ProcessFeedBenchMark.class.getName() + ".*")
        .timeUnit(TimeUnit.MILLISECONDS)
        .threads(1)

        .shouldFailOnError(true)
        .shouldDoGC(true)
        .build();

        new Runner(opt).run();
    
    static ConfigurableApplicationContext context;

    private BenchmarkTestService service;

    @Setup (Level.Trial) 
    public synchronized void  initialize() 
        try 
            String args = "";
            if(context == null) 
                context = SpringApplication.run(BenchmarkSpringBootStater.class, args );
            
            service = context.getBean(BenchmarkTestService.class);
            System.out.println(service);
         catch(Exception e) 
            e.printStackTrace();
        
    

    @Benchmark 
    public void benchmark1 (ProcessFeedBenchMark state, Blackhole bh) 
        try 
            service.li();
         catch (Exception e) 
            e.printStackTrace();
        
    

【讨论】:

我不会依赖你从这个基准测试中得到的数字。您可能依赖于服务配置和任何代理机制。此外,大部分时间都花在打印结果上。相反,您应该从该方法返回服务值并让 JMH 处理转义。这不是您应该使用 JMH 的方式。 删除 System.out.println 语句以获得 li() 方法的正确基准。 @SoumyajitSwain 你能告诉我你是如何设法将 spring boot fat jar 作为 JMH 项目的依赖项包含进来的吗? @VolodymyrBakhmatiuk - 这条线可以解决问题。 System.setProperty("java.class.path", classpath.toString());【参考方案2】:

没有必要启动 Spring 上下文来对方法进行基准测试。事实上,我不确定你真正想在这里进行基准测试,因为方法实现没有改变。如果您只想对该方法计时,那么直接调用该方法会简单得多且不易出错。如果您想在不同情况下对启动时间进行基准测试,请参阅我的答案here。

【讨论】:

如果你的方法中有spring beans的引用,是不能直接调用方法的,否则就没有你说的需要了。

以上是关于使用 JMH 对 Spring Boot 应用程序进行基准测试的主要内容,如果未能解决你的问题,请参考以下文章

JMH性能测试

在 JMH 中对具有不同值的循环进行微基准测试

用于对 Java 类进行基准测试的 JMH 与 JMeter?

springboot(二十):使用spring-boot-admin对spring-boot服务进行监控

JMH java Mocrobenchmark Harness 方法级精准测量工具 概述

涉及大量 Mysql 查询的 Java 代码的 JMH 基准测试