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

16

我有一个使用spring boot框架开发的应用程序,想要利用JMH进行性能测试。如果有任何关于这个集成的参考资料,那将非常有帮助。


1
在我看来,jmh 不适用于基准测试应用程序,而只适用于特定方法。 - Slava Semushin
1
我已经为 Spring MVC 应用程序完成了基准测试。对于 Spring Boot 应用程序也应该有方法来完成这个任务。 - Soumyajit Swain
Spring Cloud Sleuth基准样例。 - CTD
2个回答

16

解决方案比我想象的要简单。重要的是在基准测试初始化时启动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();
        }
    }
}

4
我不会依靠从这个基准测试中获得的数字。你可能要依靠服务配置和任何代理机制。此外,大部分时间都用于打印结果。相反地,你应该从方法中返回服务值,并让JMH处理逃逸。这不是使用JMH的正确方式。 - Rafael Winterhalter
为了得到li()方法的正确基准,移除了System.out.println语句。 - Soumyajit Swain
@SoumyajitSwain,你能告诉我如何将Spring Boot Fat Jar包作为JMH项目的依赖项吗? - VB_
@VolodymyrBakhmatiuk - 这行代码就是关键。System.setProperty("java.class.path", classpath.toString()); - Soumyajit Swain

0

不需要启动Spring上下文来对一个方法进行基准测试。实际上,我不确定你真正想要在这里基准测试什么,因为方法的实现并没有改变。如果你只是想计时该方法,直接调用该方法会更简单、更可靠。如果你想基准测试在不同情况下的启动时间,请参见我的答案here


2
如果您在方法内部引用了Spring Bean,则不能直接调用该方法,否则如您所提到的那样就没有必要这样做。 - Harsha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接