Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >(九)JMH的详细使用,以及压测dubbo

(九)JMH的详细使用,以及压测dubbo

作者头像
HaC
发布于 2022-01-06 06:00:46
发布于 2022-01-06 06:00:46
1.1K00
代码可运行
举报
文章被收录于专栏:HaC的技术专栏HaC的技术专栏
运行总次数:0
代码可运行

1、JMH简介

JMHJava Microbenchmark Harness,是Java用来做基准测试的一个工具,该工具由OpenJDK提供并维护,测试结果可信度高。

相对于 Jmeter、ab ,它通过编写代码的方式进行压测,在特定场景下会更能评估某项性能。

本次通过使用JMH来压测Dubbo的性能(官方也是使用JMH压测)

2、使用

只需要引用两个jar即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.29</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.29</version>
</dependency>

通过一系列的注解即可使用JMH。

@State

只能用在类上,有三个取值:

Scope.Thread:默认的State,每个测试线程分配一个实例; Scope.Benchmark:所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能; Scope.Group:每个线程组共享一个实例;

@OutputTimeUnit

时间单位,如毫秒 TimeUnit.MILLISECONDS、秒 TimeUnit.SECONDS

@Benchmark

声明一个public方法为基准测试方法。该类下的所有被@Benchmark注解的方法都会执行。

相当于类的main方法

@BenchmarkMode

指定测试某个接口的指标,如吞吐量、平均执行时间,一般我都是选择 ALL

Mode有:

  • Throughput: 整体吞吐量,例如“1秒内可以执行多少次调用” (thrpt,参加第5点)
  • AverageTime: 调用的平均时间,例如“每次调用平均耗时xxx毫秒”。(avgt)
  • SampleTime: 随机取样,最后输出取样结果的分布,例如“99%的调用在xxx毫秒以内,99.99%的调用在xxx毫秒以内”(simple)
  • SingleShotTime: 以上模式都是默认一次 iteration 是 1s,唯有 SingleShotTime 是只运行一次。往往同时把 warmup 次数设为0,用于测试冷启动时的性能。(ss)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@BenchmarkMode({Mode.Throughput,Mode.All})
public class StressTestProvider {

}

@Measurement

用于控制压测的次数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//测量2次,每次测量的持续时间为20秒
@Measurement(iterations = 2, time = 20 , timeUnit = TimeUnit.SECONDS)

@Warmup

预热,预热可以避免首次因为一些其他因素,如CPU波动、类加载耗时这些情况的影响。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)

参数解释同上。

@Fork

@Fork用于指定fork出多少个子进程来执行同一基准测试方法。

@Threads

@Threads注解用于指定使用多少个线程来执行基准测试方法,如果使用@Threads指定线程数为2,那么每次测量都会创建两个线程来执行基准测试方法。

3、运行

我这里的例子是压测dubbo,源码链接在文末

完整例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@BenchmarkMode({Mode.All})
@Warmup(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)
//测量次数,每次测量的持续时间
@Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS)
@Threads(32)
@Fork(1)
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@Slf4j
public class StressTestProvider {

    private final AnnotationConfigApplicationContext annotationConfigApplicationContext;
    private final StressTestController stressTestController;

    public StressTestProvider() {
        annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AnnotationConfig.class);
        annotationConfigApplicationContext.start();
        stressTestController = annotationConfigApplicationContext.getBean("stressTestController", StressTestController.class);
    }


    @TearDown
    public void close() throws IOException {
        annotationConfigApplicationContext.close();
    }

    @Benchmark
    public void string1k() {
        stressTestController.string1k();
    }

    @Benchmark
    public void string100k() {
        stressTestController.string100k();
    }

    public static void main(String[] args) throws RunnerException {

        log.info("测试开始");
        Options opt = new OptionsBuilder()
                .include(StressTestProvider.class.getSimpleName())
            //可以通过注解注入
//                .warmupIterations(3)
//                .warmupTime(TimeValue.seconds(10))
            //报告输出
                .result("result.json")
            //报告格式
                .resultFormat(ResultFormatType.JSON).build();
        new Runner(opt).run();
    }
}

有两种运行的方式,一般采用打成jar这种。

3.1、main方法运行

如上,只需要 配置Options,运行main方法即可,注意要使用 run模式启动,不要使用debug模式启动。

否则会报错:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
transport error 202: connect failed: Connection refused ERROR

3.2、打成jar运行

有时候需要放在服务器上运行,就需要打成一个jar,需要使用单独的jar打包插件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.2</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <finalName>jmh-demo</finalName>
                        <transformers>
                            <transformer
                                         implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>org.openjdk.jmh.Main</mainClass>
                            </transformer>
                            <transformer
                                         implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

如果不想要这种打包方式,打成jar的时候一定要声明main方法入口对应的类,也就是上面StressTestProvider

还有就是,因为我的是springboot项目,我测试了一下想同时打包springboot和 jmh:

但是运行 jhm-demo.jar 发现报错:not match main class,还是老老实实通过 profile 节点打包吧。

打完包后,通过以下命令即可运行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java -jar jmh-demo.jar  -rf json -rff result.json

-rf json 是输出 json的格式

-rff /data/result.json 是输出文件位置和名称

4、结果

执行后,会生成一个汇总结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Result "com.dubbo.benchmark.StressTestProvider.string1k":
  N = 3
  mean =      0.016 ±(99.9%) 0.022 s/op

  Histogram, s/op:
    [0.014, 0.014) = 0 
    [0.014, 0.015) = 0 
    [0.015, 0.015) = 0 
    [0.015, 0.015) = 1 
    [0.015, 0.015) = 1 
    [0.015, 0.016) = 0 
    [0.016, 0.016) = 0 
    [0.016, 0.016) = 0 
    [0.016, 0.016) = 0 
    [0.016, 0.017) = 0 
    [0.017, 0.017) = 0 
    [0.017, 0.017) = 0 
    [0.017, 0.017) = 1 
    [0.017, 0.018) = 0 
    [0.018, 0.018) = 0 
    [0.018, 0.018) = 0 

  Percentiles, s/op:
      p(0.0000) =      0.015 s/op
     p(50.0000) =      0.015 s/op
     p(90.0000) =      0.017 s/op
     p(95.0000) =      0.017 s/op
     p(99.0000) =      0.017 s/op
     p(99.9000) =      0.017 s/op
     p(99.9900) =      0.017 s/op
     p(99.9990) =      0.017 s/op
     p(99.9999) =      0.017 s/op
    p(100.0000) =      0.017 s/op

# 第36行
# Run complete. Total time: 00:05:12

Benchmark                                           Mode     Cnt     Score      Error  Units
StressTestProvider.string100k                      thrpt       3   759.794 ±   66.300  ops/s
StressTestProvider.string1k                        thrpt       3  6798.005 ± 6992.093  ops/s
StressTestProvider.string100k                       avgt       3     0.042 ±    0.002   s/op
StressTestProvider.string1k                         avgt       3     0.005 ±    0.012   s/op
StressTestProvider.string100k                     sample   22982     0.042 ±    0.001   s/op
StressTestProvider.string100k:string100k·p0.00    sample             0.017              s/op
StressTestProvider.string100k:string100k·p0.50    sample             0.041              s/op
StressTestProvider.string100k:string100k·p0.90    sample             0.048              s/op
StressTestProvider.string100k:string100k·p0.95    sample             0.050              s/op
StressTestProvider.string100k:string100k·p0.99    sample             0.058              s/op
StressTestProvider.string100k:string100k·p0.999   sample             0.075              s/op
StressTestProvider.string100k:string100k·p0.9999  sample             0.088              s/op
StressTestProvider.string100k:string100k·p1.00    sample             0.092              s/op

StressTestProvider.string1k                       sample  186906     0.005 ±    0.001   s/op
StressTestProvider.string1k:string1k·p0.00        sample             0.001              s/op
StressTestProvider.string1k:string1k·p0.50        sample             0.005              s/op
StressTestProvider.string1k:string1k·p0.90        sample             0.007              s/op
StressTestProvider.string1k:string1k·p0.95        sample             0.008              s/op
StressTestProvider.string1k:string1k·p0.99        sample             0.011              s/op
StressTestProvider.string1k:string1k·p0.999       sample             0.030              s/op
StressTestProvider.string1k:string1k·p0.9999      sample             0.035              s/op
StressTestProvider.string1k:string1k·p1.00        sample             0.038              s/op
StressTestProvider.string100k                         ss       3     0.030 ±    0.181   s/op
StressTestProvider.string1k                           ss       3     0.016 ±    0.022   s/op
     
Benchmark result is saved to result.json

结果分析

简单分析一下:

只需要从第36行开始看,我这里一共压测了2个方法

  • StressTestProvider.string100k
  • StressTestProvider.string1k
Mode

这一列表示测试的名称,也就是 @BenchmarkMode你选择的测试类型,源码在此:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public enum Mode {
    /**
     * <p>Throughput: operations per unit of time.</p>
     */
    Throughput("thrpt", "Throughput, ops/time"),

    /**
     * <p>Average time: average time per per operation.</p>
     *
     */
    AverageTime("avgt", "Average time, time/op"),

    /**
     * <p>Sample time: samples the time for each operation.</p>
     *
     */
    SampleTime("sample", "Sampling time"),

    /**
     * <p>Single shot time: measures the time for a single operation.</p>
     *
     */
    SingleShotTime("ss", "Single shot invocation time"),

thrpt:吞吐量,也可以理解为tps、ops

avgt:每次请求的平均耗时

sample:请求样本数量,这次压测一共发了多少个请求

ss:除去冷启动,一共执行了多少轮

Cnt、Score、Units

单位

Error

误差

如果你配置了输出文件,比如我上面的 resul.json ,但是你打开是看不懂的,可以借助两个网站把文件上传进行分析:

汇总:

以上对dubbo进行了分别传输1k和100k的数据压测。

provider机器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2核4g

CentOS release 6.4 (Final)
model name      : QEMU Virtual CPU version 2.5+
stepping        : 3
cpu MHz         : 2099.998
cache size      : 4096 KB

JVM:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
jdk1.8
-server -Xmx2g -Xms2g -XX:+UseG1GC 

dubbo:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
版本:2.7.3
序列化:hessian2
使用默认dubbo线程数

压测参数:

32并发

1k

100k

TPS

6700

760

RTT

95% 8ms

95% 50ms

AVGTime/OP

5ms

42ms

OOM

对比了 jmeter、Apache-Benmark(ab)、jmh 这三个压测工具,个人比较推荐使用jmh,原因有:

  • jmh压测简单,只需要引入依赖,声明注解
  • 准确性高,目前大多数性能压测都是使用jmh
  • 缺点就是代码入侵

灵感参考:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/12/29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JMH探索
JMH,即Java Microbenchmark Harness,是专门用于Java代码微基准测试的工具套件。由OpenJDK开发的,主要是基于方法层面的基准测试,精度可以达到纳秒级。当定位到热点方法,希望进一步优化方法性能的时候,就可以使用JMH对优化的结果进行量化分析。
全栈程序员站长
2022/09/18
3900
JMH探索
JMH使用指南[通俗易懂]
关于JMH,可以直接查看官网地址http://openjdk.java.net/projects/code-tools/jmh/
全栈程序员站长
2022/09/08
1.6K0
Java微基准测试神器JMH初探
当我们编写一段Java代码之后,如果想知道代码性能如何,就需要进行一些快速的性能测试。
FunTester
2023/08/04
2510
Java微基准测试神器JMH初探
JMH入门
JMH是Java Micro Benchmark Harness的简写,是专门用于代码微基准测试的工具集(toolkit)。JMH是由实现Java虚拟机的团队开发的,因此他们非常清楚开发者所编写的代码在虚拟机中将会如何执行。 由于现代JVM已经变得越来越智能,在Java文件的编译阶段、类的加载阶段,以及运行阶段都可能进行了不同程度的优化,因此开发者编写的代码在运行中未必会像自己所预期的那样具有相同的性能体现,JVM的开发者为了让普通开发者能够了解自己所编写的代码运行的情况,JMH便因此而生。
全栈程序员站长
2022/09/18
3610
Java基准测试工具JMH使用
JMH,即Java Microbenchmark Harness,这是专门用于进行代码的微基准测试的一套工具API。 JMH 由 OpenJDK/Oracle 里面那群开发了 Java 编译器的大牛们所开发 。何谓 Micro Benchmark 呢? 简单地说就是在 方法层面上的 benchmark,精度可以精确到微秒级。 本文主要介绍了性能基准测试工具 JMH,它可以通过一些功能来规避由 JVM 中的 JIT 或者其他优化对性能测试造成的影响。 Java的基准测试需要注意的几个点:
全栈程序员站长
2022/09/18
5870
Java基准测试工具JMH使用
JMH基准测试
使用JMH就可以回答第一个问题。JMH是方法级别的性能测试工具,并且是openjdk官方开发的(值得信赖),它有很多针对性能测试的功能,例如预热,该功能就可以解决StopWatch测试不准确的问题。
Yuyy
2022/09/21
3280
性能测试 —— Dubbo 基准测试
摘要: 原创出处 http://www.iocoder.cn/Performance-Testing/Dubbo-benchmark/ 「芋道源码」欢迎转载,保留摘要,谢谢!
芋道源码
2019/08/06
2.1K0
聊聊springboot项目如何利用jmh来进行基准测试
JMH(Java Microbenchmark Harness)是由OpenJDK团队开发的一个用于Java微基准测试工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。它提供了一种标准、可靠且可重复的方式来衡量Java代码的性能,包括方法调用、对象创建以及其他类型的 JVM 级别的操作。JMH 通过生成优化过的字节码来确保基准测试不受常见陷阱的影响,如热身不足、垃圾回收干扰、编译器优化等,从而产生更准确的性能指标
lyb-geek
2024/07/09
2370
聊聊springboot项目如何利用jmh来进行基准测试
【JUC进阶】10. 使用JMH进行性能测试
软件开发中,除要写出正确的代码之外,还需要写出高效的代码。这在并发编程中更加重要,原因主要有两点。首先,一部分并发程序由串行程序改造而来,其目的就是提高系统性能,因此,自然需要有一种方法对两种算法进行性能比较。其次,由于业务原因引入的多线程有可能因为线程并发控制导致性能损耗,因此要评估损耗的比重是否可以接受。无论出自何种原因需要进行性能评估,量化指标总是必要的。在大部分场合,简单地回答谁快谁慢是远远不够的,如何将程序性能量化呢? 这就是本节要介绍的 Java 微基准测试框架JMH。
有一只柴犬
2024/01/25
3660
【JUC进阶】10. 使用JMH进行性能测试
微基准测试框架JMH
最典型的场景就是你想知道两个功能相同的操作到底哪个性能比较好,通常会自己手撸一段代码,前后增加时间,然后对比多次执行的时间。这种做法比较原始,还要自己处理预热等问题。JMH提供了比较丰富的操作。且看如何使用。
李鸿坤
2020/07/17
5860
Java性能测试利器:JMH入门与实践|得物技术
在软件开发中,性能测试是不可或缺的一环。但是编写基准测试来正确衡量大型应用程序的一小部分的性能却又非常困难。当基准测试单独执行组件时,JVM或底层硬件可能会对您的组件应用许多优化。当组件作为大型应用程序的一部分运行时,这些优化可能无法应用。因此,实施不当的微基准测试可能会让您相信组件的性能比实际情况更好。编写正确的Java微基准测试通常需要防止JVM和硬件在微基准测试执行期间应用的优化,而这些优化在实际生产系统中是无法应用的。这就是JMH(Java 微基准测试工具)可以帮助您实现的功能。这篇文章我会全面给大家介绍下JMH的各个方面。
得物技术
2024/11/21
2080
顶级Java才懂的,基准测试JMH!
最近在手写一个ID生成器,需要比较UUID和目前比较流行的 NanoID之间的速度差异,当然也要测一下根据规则自创的ID生成器。
xjjdog
2021/11/19
8100
性能测试JMH
JMH,即(Java Microbenchmark Harness) 用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。 基准测试:是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试。
全栈程序员站长
2022/09/18
5120
JMH使用说明「建议收藏」
JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件。何谓Micro Benchmark呢?简单的来说就是基于方法层面的基准测试,精度可以达到微秒级。当你定位到热点方法,希望进一步优化方法性能的时候,就可以使用JMH对优化的结果进行量化的分析。和其他竞品相比——如果有的话,JMH最有特色的地方就是,它是由Oracle内部实现JIT的那拨人开发的,对于JIT以及JVM所谓的“profile guided optimization”对基准测试准确性的影响可谓心知肚明(smile)
全栈程序员站长
2022/09/08
1.2K0
JMH使用说明「建议收藏」
Dubbo 支持的几个主流序列化框架评测
今天要聊的技术是序列化,这不是我第一次写序列化相关的文章了,今天动笔之前,我还特地去博客翻了下我博客早期的一篇序列化文章(如下图),竟然都过去 4 年了。
kirito-moe
2021/09/08
1.8K0
Dubbo 支持的几个主流序列化框架评测
基准测试框架JMH使用详解
JMH即Java Microbenchmark Harness,是Java用来做基准测试的一个工具,该工具由OpenJDK提供并维护,测试结果可信度高。
全栈程序员站长
2022/09/14
7370
Java Microbenchmark Harness-Java快速入门教程
这篇快速文章重点介绍 JMH(Java Microbenchmark Harness)。首先,我们熟悉 API 并了解其基础知识。然后,我们将看到在编写微基准测试时应该考虑的一些最佳实践。
jack.yang
2025/04/05
560
不要再用main方法测试代码性能了,用这款JDK自带工具
作为软件开发人员,我们通常会写一些测试程序用来对比不同算法、不同工具的性能问题。而最常见的做法是写一个main方法,构造模拟场景进行并发测试。
程序新视界
2021/01/13
4700
性能调优必备利器之 JMH
if 快还是 switch 快?HashMap 的初始化 size 要不要指定,指定之后性能可以提高多少?各种序列化方法哪个耗时更短?
用户4172423
2020/06/12
5470
别只会 main 方法做测试,太 Low!这才是专业 Java 测试方法!
前言 “"If you cannot measure it, you cannot improve it". 在日常开发中,我们对一些代码的调用或者工具的使用会存在多种选择方式,在不确定他们性能的时候,我们首先想要做的就是去测量它。大多数时候,我们会简单的采用多次计数的方式来测量,来看这个方法的总耗时。 但是,如果熟悉JVM类加载机制的话,应该知道JVM默认的执行模式是JIT编译与解释混合执行。JVM通过热点代码统计分析,识别高频方法的调用、循环体、公共模块等,基于JIT动态编译技术,会将热点代码转换成机
java思维导图
2022/03/21
3110
相关推荐
JMH探索
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验