性能测试 —— Dubbo 基准测试

点击上方“芋道源码”,选择“设为星标”

做积极的人,而不是积极废人!

源码精品专栏

 
  • 中文详细注释的开源项目

  • RPC 框架 Dubbo 源码解析

  • 网络应用框架 Netty 源码解析

  • 消息中间件 RocketMQ 源码解析

  • 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析

  • 作业调度中间件 Elastic-Job 源码解析

  • 分布式事务中间件 TCC-Transaction 源码解析

  • Eureka 和 Hystrix 源码解析

  • Java 并发源码

摘要: 原创出处 http://www.iocoder.cn/Performance-Testing/Dubbo-benchmark/ 「芋道源码」欢迎转载,保留摘要,谢谢!

  • 1. 概述

  • 2. 性能指标

  • 3. 测试工具

  • 4. dubbo-benchmark

  • 666. 彩蛋


1. 概述

在 2019.05.21 号,在经历了 1 年多的孵化,Dubbo 终于迎来了 Apache 毕业。在这期间,Dubbo 做了比较多的功能迭代,提供了 NodeJS、Python、Go 等语言的支持,也举办了多次社区活动,在网上的“骂声”也少了。

作为一个长期使用,并且坚持使用 Dubbo 的开发者,还是比较愉快的。可能,又经历了一次技术正确的选择。当然,更愉快的是,Spring Cloud Alibaba 貌似,也孵化的差不多,双剑合并,biubiubiu 。

本文,我们就来对 Dubbo 做一次性能基准测试。当写下这句话,突然想到了徐大sao:“今天天气不错,所以来吃顿好的”。

2. 性能指标

在 Dubbo 官方团队提供的 《Dubbo 性能测试报告》 的文章里,我们比较明确的可以看到希望的性能指标:

场景名称 对应指标名称 期望值范围 实际值 是否满足期望(是/否)
1k数据 响应时间 0.9ms 0.79ms
1k数据 TPS 10000 11994

3. 测试工具

目前可用于 Dubbo 测试的工具如下:

  • dubbo-benchmark :Dubbo 官方,基于 JMH 实现的 Dubbo 性能基准测试工具。

    对 JMH 不了解的胖友,可以看看 forever alone 的基友写的 《JAVA 拾遗 — JMH 与 8 个测试陷阱》

  • jmeter-plugins-for-apache-dubbo :社区贡献,压力测试工具 Jmeter 对 Dubbo 的插件拓展。

考虑到测试的简便性,以及学习成本(大多数人不会使用 JMeter),所以我们采用 dubbo-benchmark ,虽然说 JMH 也好多人不会。但是,因为 dubbo-benchmark 提供了开箱即用的脚本,即使不了解 JMH ,也能很方便的快速上手。当然,还是希望胖友能去了解下 JMH ,毕竟是 Java 微基准测试框架,可以用来测试我们编写的很多代码的性能。

4. dubbo-benchmark

4.1 项目结构

在开始正式测试之前,我们先来了解下 dubbo-benchmark 项目的大体结构。

性能测试 —— Dubbo 基准测试_第1张图片 项目结构

分了比较多的 Maven 模块,我们将它们的关系,重新梳理如下图:

性能测试 —— Dubbo 基准测试_第2张图片 项目层级

第一层 benchmark-base

提供 Dubbo Service 的实现,如下图:

性能测试 —— Dubbo 基准测试_第3张图片 benchmark-base
  • UserService 类中,定义了我们业务场景中常用的四种方法:

     
         
    • UserServiceImpl 的实现,胖友自己看下,比较简单。

  • AbstractClient,理论来说,应该放到 client-base 中,可能迷路了。

第二层 client-base

实现 Dubbo 消费端的,基于 JMH ,实现 Benchmark 基类。重点在 benchmark.Client 类,代码如下:

private static final int CONCURRENCY = 32;public static void main(String[] args) throws Exception {    Options opt;    ChainedOptionsBuilder optBuilder = new OptionsBuilder()            // benchmark 所在的类名,此处就是 Client            .include(Client.class.getSimpleName())            // 预热 3 轮,每轮 10 秒            .warmupIterations(3)            .warmupTime(TimeValue.seconds(10))            // 测量(测试)3 轮,每轮 10 秒            .measurementIterations(3)            .measurementTime(TimeValue.seconds(10))            // 并发线程数为 32            .threads(CONCURRENCY)            // 进行 fork 的次数。如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。            .forks(1);    // 设置报告结果    opt = doOptions(optBuilder).build();    new Runner(opt).run();}private static ChainedOptionsBuilder doOptions(ChainedOptionsBuilder optBuilder) {    String output = System.getProperty("benchmark.output");    if (output != null && !output.trim().isEmpty()) {        optBuilder.output(output);    }    return optBuilder;}
  • 胖友自己看下注释。

  • 如果对 JMH 还是不了解的胖友,可以再看看如下两篇文章:

    • 《Java 微基准测试框架 JMH》

    • 《Java 并发编程笔记:JMH 性能测试框架》

在 Client 类中,定义了对 UserService 调用的四个 Benchmark 方法,代码如下:

private final ClassPathXmlApplicationContext context;private final UserService userService;public Client() {    // 读取 consumer.xml 配置文件,并启动 Spring 容器。这个配置文件,由子项目配置    context = new ClassPathXmlApplicationContext("consumer.xml");    context.start();    // 获得 UserService Bean    userService = (UserService) context.getBean("userService");}@Overrideprotected UserService getUserService() {    return userService;}@TearDownpublic void close() throws IOException {    ProtocolConfig.destroyAll();    context.close();}@Benchmark@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})@OutputTimeUnit(TimeUnit.MILLISECONDS)@Overridepublic boolean existUser() throws Exception {    return super.existUser();}@Benchmark@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})@OutputTimeUnit(TimeUnit.MILLISECONDS)@Overridepublic boolean createUser() throws Exception {    return super.createUser();}@Benchmark@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})@OutputTimeUnit(TimeUnit.MILLISECONDS)@Overridepublic User getUser() throws Exception {    return super.getUser();}@Benchmark@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})@OutputTimeUnit(TimeUnit.MILLISECONDS)@Overridepublic Page listUser() throws Exception {    return super.listUser();}

第二层 server-base

实现 Dubbo 消费端的,启动 Dubbo 服务。重点在 benchmark.Server 类,代码如下:

public class Server {    public static void main(String[] args) throws InterruptedException {        // 读取 provider.xml 配置文件,并启动 Spring 容器。这个配置文件,由子项目配置        try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml")) {            context.start();            // sleep ,防止进程结束            Thread.sleep(Integer.MAX_VALUE);        }    }}
  • 因为是被测方,所以无需集成到 JMH 中。

第三层 {protocol}-{serialize}-client

具体协议( Protocol ),使用具体序列化( Serialize ) 方式的消费者。

第四层 {protocol}-{serialize}-server

具体协议( Protocol ),使用具体序列化( Serialize ) 方式的提供者。

4.2 测试环境

  • 型号 :ecs.c5.xlarge

    艿艿:和我一样抠门(穷)的胖友,可以买竞价类型服务器,使用完后,做成镜像。等下次需要使用的时候,恢复一下。HOHO 。

  • 系统 :CentOS 7.6 64位

  • CPU :4 核

  • 内存 :8 GB

  • 磁盘 :40 GB ESSD 云盘

  • Java :OpenJDK Runtime Environment (build 1.8.0_212-b04)

  • Dubbo :2.6.1
    > 虽然 Dubbo 项目本身已经完成孵化,但是 dubbo-benchmark 并未更新到最新版本的 2.7.2 。所以,本文还是测试 Dubbo 2.6.1 版本。当然,这个对测试结果影响不大,妥妥的。

4.3 安装 dubbo-benchmark

第一步,克隆项目

git clone https://github.com/apache/dubbo-benchmark.gitcd dubbo-benchmark

第二步,启动服务提供者

sh benchmark.sh dubbo-kryo-server

会有一个编译的过程,耐心等待。

第三步,启动服务消费者

需要新启一个终端

sh benchmark.sh dubbo-kryo-client

开始 JMH 测试…整个测试过程,持续 15 分钟左右。

4.4 dubbo-hessianlite

本小节,我们来测试 dubbo-hessianlite-client 和 dubbo-hessianlite-server 。

这个组合,是我们使用 Dubbo 最主流的方式。

  • 协议:Dubbo

  • 序列化:hessian-lite ,Dubbo 对 Hessian 提供的序列化方式的性能优化和 Bug 修复。

  • 通信:Netty4

第一步,启动服务提供者

sh benchmark.sh dubbo-hessianlite-server

第二步,启动服务消费者

需要新启一个终端

sh benchmark.sh dubbo-hessianlite-client

测试结果

Benchmark                               Mode      Cnt   Score   Error   UnitsClient.createUser                      thrpt        3  16.887 ? 1.729  ops/msClient.existUser                       thrpt        3  47.293 ? 4.993  ops/msClient.getUser                         thrpt        3  19.698 ? 8.588  ops/msClient.listUser                        thrpt        3   3.457 ? 0.180  ops/msClient.createUser                       avgt        3   1.416 ? 0.308   ms/opClient.existUser                        avgt        3   0.678 ? 0.038   ms/opClient.getUser                          avgt        3   1.657 ? 0.359   ms/opClient.listUser                         avgt        3   9.299 ? 0.872   ms/opClient.createUser                     sample   499898   1.918 ? 0.007   ms/opClient.createUser:createUser?p0.00    sample            0.279           ms/opClient.createUser:createUser?p0.50    sample            1.448           ms/opClient.createUser:createUser?p0.90    sample            2.613           ms/opClient.createUser:createUser?p0.95    sample            3.027           ms/opClient.createUser:createUser?p0.99    sample            9.732           ms/opClient.createUser:createUser?p0.999   sample           16.876           ms/opClient.createUser:createUser?p0.9999  sample           28.280           ms/opClient.createUser:createUser?p1.00    sample           39.453           ms/opClient.existUser                      sample  1376160   0.697 ? 0.002   ms/opClient.existUser:existUser?p0.00      sample            0.094           ms/opClient.existUser:existUser?p0.50      sample            0.647           ms/opClient.existUser:existUser?p0.90      sample            0.842           ms/opClient.existUser:existUser?p0.95      sample            0.921           ms/opClient.existUser:existUser?p0.99      sample            1.425           ms/opClient.existUser:existUser?p0.999     sample           10.355           ms/opClient.existUser:existUser?p0.9999    sample           16.145           ms/opClient.existUser:existUser?p1.00      sample           24.773           ms/opClient.getUser                        sample   568869   1.686 ? 0.006   ms/opClient.getUser:getUser?p0.00          sample            0.262           ms/opClient.getUser:getUser?p0.50          sample            1.436           ms/opClient.getUser:getUser?p0.90          sample            1.954           ms/opClient.getUser:getUser?p0.95          sample            2.609           ms/opClient.getUser:getUser?p0.99          sample            9.634           ms/opClient.getUser:getUser?p0.999         sample           15.862           ms/opClient.getUser:getUser?p0.9999        sample           31.217           ms/opClient.getUser:getUser?p1.00          sample           44.302           ms/opClient.listUser                       sample   103394   9.272 ? 0.038   ms/opClient.listUser:listUser?p0.00        sample            1.792           ms/opClient.listUser:listUser?p0.50        sample            9.060           ms/opClient.listUser:listUser?p0.90        sample           14.287           ms/opClient.listUser:listUser?p0.95        sample           15.679           ms/opClient.listUser:listUser?p0.99        sample           17.336           ms/opClient.listUser:listUser?p0.999       sample           30.966           ms/opClient.listUser:listUser?p0.9999      sample           38.161           ms/opClient.listUser:listUser?p1.00        sample           45.351           ms/op

4.5 dubbo-fst

本小节,我们来测试 dubbo-fst-client 和 dubbo-fst-server 。

这个组合,是我们使用 Dubbo 最主流的方式。

  • 协议:Dubbo

  • 序列化:FST

  • 通信:Netty4

第一步,启动服务提供者

sh benchmark.sh dubbo-fst-server

第二步,启动服务消费者

需要新启一个终端

sh benchmark.sh dubbo-fst-client

测试结果

Benchmark                               Mode      Cnt   Score    Error   UnitsClient.createUser                      thrpt        3  44.810 ?  5.152  ops/msClient.existUser                       thrpt        3  53.153 ? 49.787  ops/msClient.getUser                         thrpt        3  41.754 ?  8.210  ops/msClient.listUser                        thrpt        3  14.791 ?  3.458  ops/msClient.createUser                       avgt        3   0.719 ?  0.080   ms/opClient.existUser                        avgt        3   0.574 ?  0.434   ms/opClient.getUser                          avgt        3   0.703 ?  0.045   ms/opClient.listUser                         avgt        3   2.189 ?  0.353   ms/opClient.createUser                     sample  1267915   0.756 ?  0.002   ms/opClient.createUser:createUser?p0.00    sample            0.099            ms/opClient.createUser:createUser?p0.50    sample            0.678            ms/opClient.createUser:createUser?p0.90    sample            0.888            ms/opClient.createUser:createUser?p0.95    sample            1.034            ms/opClient.createUser:createUser?p0.99    sample            3.383            ms/opClient.createUser:createUser?p0.999   sample           10.502            ms/opClient.createUser:createUser?p0.9999  sample           22.650            ms/opClient.createUser:createUser?p1.00    sample           35.979            ms/opClient.existUser                      sample  1461428   0.656 ?  0.002   ms/opClient.existUser:existUser?p0.00      sample            0.077            ms/opClient.existUser:existUser?p0.50      sample            0.504            ms/opClient.existUser:existUser?p0.90      sample            1.128            ms/opClient.existUser:existUser?p0.95      sample            1.516            ms/opClient.existUser:existUser?p0.99      sample            2.802            ms/opClient.existUser:existUser?p0.999     sample            6.452            ms/opClient.existUser:existUser?p0.9999    sample           33.358            ms/opClient.existUser:existUser?p1.00      sample           58.262            ms/opClient.getUser                        sample  1270938   0.755 ?  0.003   ms/opClient.getUser:getUser?p0.00          sample            0.084            ms/opClient.getUser:getUser?p0.50          sample            0.588            ms/opClient.getUser:getUser?p0.90          sample            1.034            ms/opClient.getUser:getUser?p0.95          sample            1.626            ms/opClient.getUser:getUser?p0.99          sample            4.473            ms/opClient.getUser:getUser?p0.999         sample           10.830            ms/opClient.getUser:getUser?p0.9999        sample           27.719            ms/opClient.getUser:getUser?p1.00          sample           45.875            ms/opClient.listUser                       sample   442763   2.166 ?  0.009   ms/opClient.listUser:listUser?p0.00        sample            0.306            ms/opClient.listUser:listUser?p0.50        sample            1.767            ms/opClient.listUser:listUser?p0.90        sample            3.039            ms/opClient.listUser:listUser?p0.95        sample            4.415            ms/opClient.listUser:listUser?p0.99        sample           11.551            ms/opClient.listUser:listUser?p0.999       sample           21.045            ms/opClient.listUser:listUser?p0.9999      sample           32.702            ms/opClient.listUser:listUser?p1.00        sample           45.089            ms/op

4.6 dubbo-kryo

本小节,我们来测试 dubbo-kryo-client 和 dubbo-kryo-server 。

  • 协议:Dubbo

  • 序列化:Kryo

  • 通信:Netty4

第一步,启动服务提供者

sh benchmark.sh dubbo-kryo-server

第二步,启动服务消费者

需要新启一个终端

sh benchmark.sh dubbo-kryo-client

测试结果

Benchmark                               Mode      Cnt   Score   Error   UnitsClient.createUser                      thrpt        3  33.678 ? 2.656  ops/msClient.existUser                       thrpt        3  50.030 ? 3.509  ops/msClient.getUser                         thrpt        3  34.125 ? 4.886  ops/msClient.listUser                        thrpt        3  11.929 ? 1.746  ops/msClient.createUser                       avgt        3   0.955 ? 0.164   ms/opClient.existUser                        avgt        3   0.642 ? 0.051   ms/opClient.getUser                          avgt        3   0.940 ? 0.071   ms/opClient.listUser                         avgt        3   2.603 ? 0.748   ms/opClient.createUser                     sample   985106   0.973 ? 0.003   ms/opClient.createUser:createUser?p0.00    sample            0.148           ms/opClient.createUser:createUser?p0.50    sample            0.855           ms/opClient.createUser:createUser?p0.90    sample            1.147           ms/opClient.createUser:createUser?p0.95    sample            1.315           ms/opClient.createUser:createUser?p0.99    sample            5.300           ms/opClient.createUser:createUser?p0.999   sample           12.517           ms/opClient.createUser:createUser?p0.9999  sample           21.037           ms/opClient.createUser:createUser?p1.00    sample           31.850           ms/opClient.existUser                      sample  1470527   0.652 ? 0.001   ms/opClient.existUser:existUser?p0.00      sample            0.092           ms/opClient.existUser:existUser?p0.50      sample            0.601           ms/opClient.existUser:existUser?p0.90      sample            0.800           ms/opClient.existUser:existUser?p0.95      sample            0.876           ms/opClient.existUser:existUser?p0.99      sample            1.550           ms/opClient.existUser:existUser?p0.999     sample            9.650           ms/opClient.existUser:existUser?p0.9999    sample           14.844           ms/opClient.existUser:existUser?p1.00      sample           30.573           ms/opClient.getUser                        sample  1001893   0.957 ? 0.004   ms/opClient.getUser:getUser?p0.00          sample            0.127           ms/opClient.getUser:getUser?p0.50          sample            0.741           ms/opClient.getUser:getUser?p0.90          sample            1.401           ms/opClient.getUser:getUser?p0.95          sample            2.191           ms/opClient.getUser:getUser?p0.99          sample            5.546           ms/opClient.getUser:getUser?p0.999         sample           12.059           ms/opClient.getUser:getUser?p0.9999        sample           24.518           ms/opClient.getUser:getUser?p1.00          sample           49.873           ms/opClient.listUser                       sample   363958   2.636 ? 0.013   ms/opClient.listUser:listUser?p0.00        sample            0.390           ms/opClient.listUser:listUser?p0.50        sample            2.071           ms/opClient.listUser:listUser?p0.90        sample            4.084           ms/opClient.listUser:listUser?p0.95        sample            6.947           ms/opClient.listUser:listUser?p0.99        sample           12.403           ms/opClient.listUser:listUser?p0.999       sample           22.940           ms/opClient.listUser:listUser?p0.9999      sample           40.935           ms/opClient.listUser:listUser?p1.00        sample           60.097           ms/op

4.7 小结

可能有胖友,对 JMH 的结果报告不熟悉,这里简单介绍下:

  • Mode :thrpt ,throughput 的缩写,吞吐量,单位为:ops/ms ,所以需要乘以 1000 ,换算成 QPS 。

  • Mode :avgt ,AverageTime 的缩写,每个操作?的时间,单位为 ms 毫秒。

  • Mode :sample ,采样分布。每一行代表,代表百分之多少(?p)的请求,在多少毫秒内。

整理结果如下表:

性能测试 —— Dubbo 基准测试_第4张图片 性能结果
  • 三个测试用例,差别是序列化使用的库,所以序列化的性能,决定了整体的性能结果。

  • 方法的性能排行是:existUser > getUser > createUser > listUser

  • 项目的性能排行是:dubbo-fst > dubbo-kryo > dubbo-hessianlite

当然,得到这样一个测试结果,我们很自然的会有一个疑惑,既然 FST 和 Kryo 性能比 Hessian Lite 好,为什么默认选择的还是 Hessian Lite 呢?因为在 RPC 场景下,我们有跨语言的诉求,而 FST 和 Kryo 是 Java 序列化的库,不支持跨语言,而 Hessian Lite 支持。

666. 彩蛋

因为 dubbo-benchmark 项目的存在,所以整个测试的过程,体验还是比较舒服,测试也是非常顺利的。当然,还是比较期待 dubbo-benchmark 后续能更新的更牛逼:

  • 例如说,支持更多的序列化方式,因为最新版本有了 Gson、Probufstuff 等新的序列化支持

  • 再例如说,支持除了 Dubbo 协议之外,Rest 等等。

也推荐下如下文章:

  • 《Dubbo 容量规划》

  • 《性能测试工具 Jmeter 测试 Dubbo 接口脚本编写》

  • 《在Dubbo中使用高效的 Java 序列化(Kryo 和 FST)》

  • 《基于 Dubbo 框架下的 RPC 通讯协议性能测试》

  • 《孰优孰劣?Dubbo VS Spring Cloud 性能测试大对决!》



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

640?wx_fmt=jpeg

已在知识星球更新源码解析如下:

640?wx_fmt=png

640?wx_fmt=png

640?wx_fmt=png

如果你喜欢这篇文章,喜欢,转发。

生活很美好,明天见(。・ω・。)ノ♡


你可能感兴趣的:(性能测试 —— Dubbo 基准测试)