Jmeter进行http接口压力测试:
https://www.cnblogs.com/badaoliumangqizhi/p/16301432.html
JMH,全称Java Microbenchmark Harness (微基准测试框架),是专门用于Java代码微基准测试的一套测试工具API,
是由Java虚拟机团队开发的的,一般用于代码的性能调优。
MicroBenchmark就是在method层面上的benchmark,精度可以精确到微秒级、甚至可以达到纳秒级别,
适用于 java 以及其他基于 JVM 的语言。与Apache JMeter 不同,JMH 测试的对象可以是任一方法,颗粒度更小,
而不仅限于接口以及API层面。
想要知道某个函数需要执行多长时间,以及执行时间和输入之间的相关性
想要对比接口不同实现在给定条件下的吞吐量大小
想要知道百分之N的请求在多长时间内完成
想要找出了热点函数,需要对热点函数进行进一步优化时
针对于函数的多种实现方式(例如JSON序列化/反序列化有Jackson和Gson实现),不知道哪种实现性能更好
JMH官方仓库:
GitHub - openjdk/jmh: https://openjdk.org/projects/code-tools/jmh
JMH官网示例demo:
code-tools/jmh: 2be2df7dbaf8 jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_01_HelloWorld.java
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
1、首先如果jdk小于1.9,则需要添加依赖
org.openjdk.jmh
jmh-core
1.23
org.openjdk.jmh
jmh-generator-annprocess
1.23
2、参考上面官网提供helloworld的demo
package org.openjdk.jmh.samples;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
public class JMHSample_01_HelloWorld {
@Benchmark
public void wellHelloThere() {
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_01_HelloWorld.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
}
可知需要新建类,并在main方法中这样写去启动基准测试和执行测试。
然后需要进行测试的方法添加@Benchmark注解。
3、按照上面官网提供的示例,对比ArrayList与LinkedList在头部进行添加时的性能测试添加一些细节
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
//测试完成时间
@BenchmarkMode(Mode.AverageTime)
//设置统计结果的时间单位
@OutputTimeUnit(TimeUnit.NANOSECONDS)
//预热所需要配置的一些基本测试参数
@Warmup(iterations = 2,time = 1,timeUnit = TimeUnit.SECONDS)
//测试次数和时间
@Measurement(iterations = 5,time = 5,timeUnit = TimeUnit.SECONDS)
//fork一个线程
@Fork(1)
//通过 State 可以指定一个对象的作用范围
@State(Scope.Thread)
public class ArrayListWithLinkedListTestHeaderAdd {
private static final int maxSize = 10000; //测试循环次数
private static final int operationSize = 100; //操作次数
private static ArrayList arrayList;
private static LinkedList linkedList;
public static void main(String[] args) throws RunnerException {
//启动基准测试
Options opt = new OptionsBuilder()
.include(ArrayListWithLinkedListTestHeaderAdd.class.getSimpleName()) //要导入的测试类
.build();
//执行测试
new Runner(opt).run();
}
//@Setup作用于方法上,用于测试前的初始化工作
@Setup
public void init(){
arrayList = new ArrayList();
linkedList = new LinkedList();
for (int i = 0; i < maxSize; i++) {
arrayList.add(i);
linkedList.add(i);
}
}
//用于回收某些资源
@TearDown
public void finish(){
}
@Benchmark
public void addArrayByFirst(Blackhole blackhole){
for (int i = 0; i < operationSize; i++) {
arrayList.add(i,i);
}
//为了避免JIT忽略未被使用的结果计算/为了避免死码消除问题
blackhole.consume(arrayList);
}
@Benchmark
public void addLinkedByFirst(Blackhole blackhole){
for (int i = 0; i < operationSize; i++) {
linkedList.add(i,i);
}
//为了避免JIT忽略未被使用的结果计算/为了避免死码消除问题
blackhole.consume(linkedList);
}
}
这里加了一些注解,并配置了一些参数。
4、JMH部分注解说明
@Benchmark
需要测试的方法,添加该注解。
@BenchmarkMode
用来配置 Mode 选项,可用于类或者方法上,这个注解的 value 是一个数组,可以把几种 Mode 集合在一起执行,如:
@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}),还可以设置为 Mode.All,即全部执行一遍。
Throughput:整体吞吐量,每秒执行了多少次调用,单位为 ops/time
AverageTime:用的平均时间,每次操作的平均时间,单位为 time/op
SampleTime:随机取样,最后输出取样结果的分布
SingleShotTime:只运行一次,往往同时把 Warmup 次数设为 0,用于测试冷启动时的性能
All:上面的所有模式都执行一次
@State
通过 State 可以指定一个对象的作用范围,JMH 根据 scope 来进行实例化和共享操作。
@State 可以被继承使用,如果父类定义了该注解,子类则无需定义。
由于 JMH 允许多线程同时执行测试,不同的选项含义如下:
Scope.Benchmark:所有测试线程共享一个实例,测试有状态实例在多线程共享下的性能
Scope.Group:同一个线程在同一个 group 里共享实例
Scope.Thread:默认的 State,每个测试线程分配一个实例
@OutputTimeUnit
为统计结果的时间单位,可用于类或者方法注解
@Warmup
预热所需要配置的一些基本测试参数,可用于类或者方法上。一般前几次进行程序测试的时候都会比较慢,
所以要让程序进行几轮预热,保证测试的准确性。参数如下所示:
iterations:预热的次数
time:每次预热的时间
timeUnit:时间的单位,默认秒
batchSize:批处理大小,每次操作调用几次方法
为什么需要预热?
因为 JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译为机器码,从而提高执行速度,
所以为了让 benchmark 的结果更加接近真实情况就需要进行预热。
@Measurement
实际调用方法所需要配置的一些基本测试参数,可用于类或者方法上,参数和 @Warmup 相同。
@Threads
每个进程中的测试线程,可用于类或者方法上。
@Fork
进行 fork 的次数,可用于类或者方法上。如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。
@Param
指定某项参数的多种情况,特别适合用来测试一个函数在不同的参数输入的情况下的性能,只能作用在字段上,
使用该注解必须定义@State 注解。
@Setup
方法注解,会在执行 benchmark 之前被执行,正如其名,主要用于初始化。
@TearDown
方法注解,与@Setup 相对的,会在所有 benchmark 执行结束以后执行,主要用于资源的回收等
5、运行上面的main方法,对比测试两个方法的性能