多线程之JMH测试

介绍

JMH 是 Java Microbenchmark Harness 的缩写。中文意思大致是 “JAVA 微基准测试套件”

优势

  1. 可重复性:可进行重复性的测试,这样做有利于比较每次的测试结果,得到性能结果的长期变化趋势,为系统调优和上线前的容量规划做参考。

  2. 可观测性:通过全方位的监控(包括测试开始到结束,执行机、服务器、数据库),及时了解和分析测试过程发生了什么。

  3. 可展示性:相关人员可以直观明了的了解测试结果(web界面、仪表盘、折线图树状图等形式)。

  4. 真实性:测试的结果反映了客户体验到的真实的情况(真实准确的业务场景+与生产一致的配置+合理正确的测试方法)

  5. 可执行性:相关人员可以快速的进行测试验证修改调优(可定位可分析)

注解

@Benchmark

用于声明是测试

@Warmup

预热 :iterations次数,time时间

@Warmup(iterations = 1, time = 3)

@Fork(5)

用多少线程进行执行

@BenchmarkMode(Mode.Throughput)

基准测试的模式,

测试模式: Throughput(吞吐量), AverageTime(平均时间),SampleTime(在测试中,随机进行采样执行的时间),SingleShotTime(在每次执行中计算耗时),All

@Measurement (iterations = 1, time = 3)

方法调用多少次,多少时间

配置

  1. 添加依赖
      <dependency>
               <groupId>org.openjdk.jmh</groupId>
               <artifactId>jmh-core</artifactId>
               <version>1.21</version>
           </dependency>
   
           <!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
           <dependency>
               <groupId>org.openjdk.jmh</groupId>
               <artifactId>jmh-generator-annprocess</artifactId>
               <version>1.21</version>
               <scope>test</scope>
           </dependency>
  1. 配置idea

idea安装JMH插件 JMH plugin v1.0.3

配置可以识别注解

compiler -> Annotation Processors -> Enable Annotation Processing

  1. 写测试

需在test/下面

    @Benchmark
    @Warmup(iterations = 1, time = 3)
    @Fork(5)
    @BenchmarkMode(Mode.Throughput)
    @Measurement(iterations = 1, time = 3)
    public void testForEach() {
        PS.foreach();
    }
  1. 测试结果展示

# JMH version: 1.21
# VM version: JDK 1.8.0_201, Java HotSpot(TM) 64-Bit Server VM, 25.201-b09
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 1 iterations, 3 s each
# Measurement: 1 iterations, 3 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.mashibing.jmh.PSTest.testForEach

# 第一次
# Run progress: 0.00% complete, ETA 00:00:30
# Fork: 1 of 5
# Warmup Iteration   1: 7.122 ops/s
Iteration   1: 7.964 ops/s
# 第二次
# Run progress: 20.00% complete, ETA 00:00:26
# Fork: 2 of 5
# Warmup Iteration   1: 9.070 ops/s
Iteration   1: 7.902 ops/s
# 第三次
# Run progress: 40.00% complete, ETA 00:00:19
# Fork: 3 of 5
# Warmup Iteration   1: 8.458 ops/s
Iteration   1: 8.374 ops/s
# 第四次
# Run progress: 60.00% complete, ETA 00:00:13
# Fork: 4 of 5
# Warmup Iteration   1: 10.739 ops/s
Iteration   1: 10.859 ops/s
# 第五次
# Run progress: 80.00% complete, ETA 00:00:06
# Fork: 5 of 5
# Warmup Iteration   1: 8.819 ops/s
Iteration   1: 8.868 ops/s


Result "com.mashibing.jmh.PSTest.testForEach":
  8.794 ±(99.9%) 4.689 ops/s [Average]
  (min, avg, max) = (7.902, 8.794, 10.859), stdev = 1.218
  CI (99.9%): [4.105, 13.482] (assumes normal distribution)


# Run complete. Total time: 00:00:32

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
PSTest.testForEach  thrpt    5  8.794 ± 4.689  ops/s

Process finished with exit code 0

<think>嗯,用户现在问的是怎么用JMH测试Dubbo代码。首先,我得回忆一下JMH是什么。JMH是Java的微基准测试工具,专门用来做性能测试的,特别是那些需要高精度测量的场景。Dubbo是一个分布式服务框架,主要用于服务之间的RPC调用。用户可能是在开发Dubbo服务时遇到了性能问题,或者想优化他们的服务调用,所以需要用JMH测试性能。 接下来,我需要考虑用户可能的使用场景。他们可能是在开发一个高并发的应用,需要确保Dubbo服务的性能达标,或者他们正在尝试不同的配置参数,比如线程池大小、序列化方式等,想通过JMH来比较不同配置下的性能差异。另外,也有可能他们在优化代码,比如改进服务实现,需要量化优化前后的性能变化。 然后,用户的需求可能不仅仅是简单的配置JMH,还可能涉及到如何正确设置Dubbo的服务提供者和消费者,在测试环境中模拟真实调用,避免网络延迟等因素干扰。因为JMH测试通常是在本地运行,而Dubbo通常涉及远程调用,所以可能需要使用Dubbo的本地调用模式(如injvm协议)来消除网络影响,确保测试结果准确反映代码本身的性能。 接下来,我应该思考如何整合JMH和Dubbo。首先,用户需要添加JMH的依赖到项目中,配置好Dubbo的服务,然后在JMH测试类中启动Dubbo的服务提供者,并在测试方法中调用消费者进行基准测试。需要注意Dubbo的上下文环境是否会在多次测试中保持一致,是否需要每次测试前重新初始化服务,或者是否有状态残留的问题。 另外,JMH测试需要预热,多次迭代,这些都需要在测试代码中正确配置。比如,使用@Warmup和@Measurement注解来设置预热和测量的次数及时间。还要注意避免JIT优化对测试结果的影响,比如确保测试方法有返回值,避免死码消除。 可能遇到的问题包括Dubbo服务未正确启动导致测试失败,或者测试过程中出现超时、序列化问题等。需要指导用户如何排查这些问题,比如检查Dubbo配置是否正确,服务是否注册到注册中心(或者在本地模式下是否正确使用injvm),以及JMH测试用例是否正确编写。 此外,用户可能需要了解如何分析JMH的输出结果,比如吞吐量、平均响应时间、百分位数等指标,从而评估Dubbo服务的性能表现。还可能想知道如何调整JMH参数以获得更准确的测试结果,比如增加测试的迭代次数,调整线程数等。 最后,总结一下,回答需要涵盖JMH的基本配置步骤,Dubbo的本地测试设置,编写基准测试类的方法,常见问题及解决方案,以及如何解读测试结果。这样用户能够全面了解如何利用JMH测试和优化他们的Dubbo代码。</think>以下是使用JMH进行Dubbo代码性能测试的深度指南(附完整实现方案): --- ### **一、核心测试原理** 通过JMH的**状态管理机制**实现Dubbo服务的精准性能测量: - **@State(Scope.Benchmark)**:保持单次基准测试中Dubbo服务实例唯一性 - **@Setup**:建立服务提供者与消费者的连接(模拟真实环境) - **@TearDown**:释放资源避免内存泄漏 --- ### **二、关键配置步骤** #### 1. 依赖配置(Maven) ```xml <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.37</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>3.2.6</version> </dependency> <!-- 使用本地注册中心减少干扰 --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-simple</artifactId> <version>3.2.6</version> </dependency> ``` #### 2. 基准测试类结构 ```java @BenchmarkMode(Mode.Throughput) // 吞吐量模式 @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Benchmark) public class DubboBenchmark { private ReferenceConfig<DemoService> referenceConfig; private DemoService demoService; @Setup public void init() { // 服务提供者配置 ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE)); service.export(); // 服务消费者配置 referenceConfig = new ReferenceConfig<>(); referenceConfig.setInterface(DemoService.class); referenceConfig.setScope("local"); // 强制本地调用 demoService = referenceConfig.get(); } @Benchmark public String rpcCall() { return demoService.sayHello("JMH"); } @TearDown public void shutdown() { referenceConfig.destroy(); } } ``` --- ### **三、优化测试精度** 1. **消除网络延迟干扰** ```java // 强制使用in-JVM协议 referenceConfig.setUrl("injvm://127.0.0.1/com.example.DemoService"); ``` 2. **序列化优化配置** ```properties dubbo.protocol.serialization=hessian2 ``` 3. **线程池隔离** ```java @Threads(4) // 模拟并发线程数 @Fork(2) // 进程隔离次数 ``` --- ### **四、执行与结果分析** #### 1. 启动参数 ```bash mvn clean install && java -jar target/benchmarks.jar -rf json ``` #### 2. 典型输出解读 ```json { "benchmark": "DubboBenchmark.rpcCall", "mode": "thrpt", "score": 32567.342, // 每秒操作数 "scoreError": ±125.67, "params": { "threads": "4" } } ``` --- ### **五、性能瓶颈定位** | 现象 | 排查方向 | 优化建议 | |--------------------|----------------------------|----------------------------| | QPS低于预期 | 序列化/反序列化耗时 | 切换为kryo或fst序列化 | | 吞吐量波动大 | 线程池满/队列堆积 | 调整dubbo线程池参数 | | 长尾响应明显 | 同步调用阻塞 | 改为CompletableFuture异步调用 | --- ### **六、高级测试场景** #### 1. 集群调用测试 ```java // 构建多个服务实例 List<ServiceConfig> services = IntStream.range(0, 3) .mapToObj(i -> new ServiceConfig<>().setRef(new DemoServiceImpl())) .collect(Collectors.toList()); services.forEach(ServiceConfig::export); ``` #### 2. 熔断降级测试 ```java @Benchmark public String fallbackTest() { try { return demoService.unstableMethod(); } catch (RpcException e) { return "fallback"; // 测量异常处理性能 } } ``` --- ### **七、注意事项** 1. **JVM预热配置** ```java @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) ``` 2. **防止方法内联优化** ```java @CompilerControl(CompilerControl.Mode.DONT_INLINE) ``` 3. **Dubbo配置验证** ```bash telnet 127.0.0.1 20880 # 检查端口监听状态 ``` 通过这种配置方式,可以精确测量Dubbo核心逻辑的执行效率,为性能优化提供可靠数据支撑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多多洛码代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值