长期处于CRUD工做中的我忽然有一天关心起本身项目的qps
了.便用jmeter
测试了访问量最大的接口,发现只有可怜的17r/s
左右......看到网络上比比皆是的几百万qps
,我无地自容啊.java
因而就准备进行性能优化了,不过在优化前咱们须要进行性能测试,这样才能知道优化的效果如何.好比我第一步就是用redis
加了缓存,结果测试发现竟然比不加以前还要慢???因此性能测试是很是重要的一环,而jmh
就是很是适合的性能测试工具了.redis
准备工做很是的简单,引入jmh
的maven
包就能够了.spring
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.22</version>
<scope>provided</scope>
</dependency>
复制代码
package jmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
/** * Benchmark * * @author wangpenglei * @since 2019/11/27 13:54 */
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class Benchmark {
public static void main(String[] args) throws Exception {
// 使用一个单独进程执行测试,执行5遍warmup,而后执行5遍测试
Options opt = new OptionsBuilder().include(Benchmark.class.getSimpleName()).forks(1).warmupIterations(5)
.measurementIterations(5).build();
new Runner(opt).run();
}
@Setup
public void init() {
}
@TearDown
public void down() {
}
@org.openjdk.jmh.annotations.Benchmark public void test() {
}
}
复制代码
这个注解决定了测试模式,具体的内容网络上很是多,我这里采用的是计算平均运行时间数据库
这个注解是最后输出结果时的单位.由于测试的是接口,因此我采用的是毫秒.若是是测试本地redis
或者本地方法这种能够换更小的单位.缓存
这个注解定义了给定类实例的可用范围,由于spring
里的bean
默认是单例,因此我这里采用的是运行相同测试的全部线程将共享实例。能够用来测试状态对象的多线程性能(或者仅标记该范围的基准).性能优化
很是简单的注解,日常测试都有的测试前初始化*测试后清理资源**测试方法*.bash
由于咱们须要spring
的环境才能测试容器里的bean
,因此须要在初始化方法中手动建立一个.我查了一下资料没发现什么更好的方法,就先本身手动建立吧.服务器
private ConfigurableApplicationContext context;
private AppGoodsController controller;
@Setup
public void init() {
// 这里的WebApplication.class是项目里的spring boot启动类
context = SpringApplication.run(WebApplication.class);
// 获取须要测试的bean
this.controller = context.getBean(AppGoodsController.class);
}
@TearDown
public void down() {
context.close();
}
复制代码
写好测试方法后启动main
方法就开始测试了,如今会报一些奇奇怪怪的错误,不过不影响结果我就没管了.运行完成后会输出结果,这时候能够对比下优化的效果.网络
Result "jmh.Benchmark.testGetGoodsList":
65.969 ±(99.9%) 10.683 ms/op [Average]
(min, avg, max) = (63.087, 65.969, 69.996), stdev = 2.774
CI (99.9%): [55.286, 76.652] (assumes normal distribution)
# Run complete. Total time: 00:02:48
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
Benchmark Mode Cnt Score Error Units
Benchmark.testGetGoodsList avgt 5 65.969 ± 10.683 ms/op
Process finished with exit code 0
复制代码
在文章开头我讲了一个负优化的例子,我用redis
加了缓存后竟然比直接数据库查询还要慢!其实缘由很简单,我在本地电脑上测试,链接的redis
却部署在服务器上.这样来回公网的网络延迟就已经很大了.不过数据库也是经过公网的,也不会比redis
快才对.最后的缘由是发现部署redis
的服务器带宽只有1m
也就是100kb/s
,很容易就被占满了.最后优化是redis
加缓存与使用内网链接redis
.多线程
Result "jmh.Benchmark.testGetGoodsList":
102.419 ±(99.9%) 153.083 ms/op [Average]
(min, avg, max) = (65.047, 102.419, 162.409), stdev = 39.755
CI (99.9%): [≈ 0, 255.502] (assumes normal distribution)
# Run complete. Total time: 00:03:03
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
Benchmark Mode Cnt Score Error Units
Benchmark.testGetGoodsList avgt 5 102.419 ± 153.083 ms/op
Process finished with exit code 0
复制代码
redis
速度,连的是本地redis
):Result "jmh.Benchmark.testGetGoodsList":
29.210 ±(99.9%) 2.947 ms/op [Average]
(min, avg, max) = (28.479, 29.210, 30.380), stdev = 0.765
CI (99.9%): [26.263, 32.157] (assumes normal distribution)
# Run complete. Total time: 00:02:49
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
Benchmark Mode Cnt Score Error Units
Benchmark.testGetGoodsList avgt 5 29.210 ± 2.947 ms/op
Process finished with exit code 0
复制代码
能够看到大约快了3.5
倍,其实还有优化空间,所有数据库操做都经过redis
缓存的话,大概1ms
就处理完了.