java kryopt_性能测试 —— Dubbo 基准测试

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.9ms0.79ms是

1k数据TPS1000011994是

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 项目的大体结构。

项目结构

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

项目层级

第一层 benchmark-base

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

java kryopt_性能测试 —— Dubbo 基准测试_第1张图片

benchmark-baseUserService 类中,定义了我们业务场景中常用的四种方法:public interface UserService {public boolean existUser(String email);

public boolean createUser(User user);

public User getUser(long id);

public Page listUser(int pageNo);}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");

}

@Override

protected UserService getUserService(){

return userService;

}

@TearDown

public void close() throws IOException{

ProtocolConfig.destroyAll();

context.close();

}

@Benchmark

@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})

@OutputTimeUnit(TimeUnit.MILLISECONDS)

@Override

public boolean existUser() throws Exception{

return super.existUser();

}

@Benchmark

@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})

@OutputTimeUnit(TimeUnit.MILLISECONDS)

@Override

public boolean createUser() throws Exception{

return super.createUser();

}

@Benchmark

@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})

@OutputTimeUnit(TimeUnit.MILLISECONDS)

@Override

public User getUser() throws Exception{

return super.getUser();

}

@Benchmark

@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})

@OutputTimeUnit(TimeUnit.MILLISECONDS)

@Override

public 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.git

cd 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   Units

Client.createUser                      thrpt        3  16.887 ? 1.729  ops/ms

Client.existUser                       thrpt        3  47.293 ? 4.993  ops/ms

Client.getUser                         thrpt        3  19.698 ? 8.588  ops/ms

Client.listUser                        thrpt        3   3.457 ? 0.180  ops/ms

Client.createUser                       avgt        3   1.416 ? 0.308   ms/op

Client.existUser                        avgt        3   0.678 ? 0.038   ms/op

Client.getUser                          avgt        3   1.657 ? 0.359   ms/op

Client.listUser                         avgt        3   9.299 ? 0.872   ms/op

Client.createUser                     sample   499898   1.918 ? 0.007   ms/op

Client.createUser:createUser?p0.00    sample            0.279           ms/op

Client.createUser:createUser?p0.50    sample            1.448           ms/op

Client.createUser:createUser?p0.90    sample            2.613           ms/op

Client.createUser:createUser?p0.95    sample            3.027           ms/op

Client.createUser:createUser?p0.99    sample            9.732           ms/op

Client.createUser:createUser?p0.999   sample           16.876           ms/op

Client.createUser:createUser?p0.9999  sample           28.280           ms/op

Client.createUser:createUser?p1.00    sample           39.453           ms/op

Client.existUser                      sample  1376160   0.697 ? 0.002   ms/op

Client.existUser:existUser?p0.00      sample            0.094           ms/op

Client.existUser:existUser?p0.50      sample            0.647           ms/op

Client.existUser:existUser?p0.90      sample            0.842           ms/op

Client.existUser:existUser?p0.95      sample            0.921           ms/op

Client.existUser:existUser?p0.99      sample            1.425           ms/op

Client.existUser:existUser?p0.999     sample           10.355           ms/op

Client.existUser:existUser?p0.9999    sample           16.145           ms/op

Client.existUser:existUser?p1.00      sample           24.773           ms/op

Client.getUser                        sample   568869   1.686 ? 0.006   ms/op

Client.getUser:getUser?p0.00          sample            0.262           ms/op

Client.getUser:getUser?p0.50          sample            1.436           ms/op

Client.getUser:getUser?p0.90          sample            1.954           ms/op

Client.getUser:getUser?p0.95          sample            2.609           ms/op

Client.getUser:getUser?p0.99          sample            9.634           ms/op

Client.getUser:getUser?p0.999         sample           15.862           ms/op

Client.getUser:getUser?p0.9999        sample           31.217           ms/op

Client.getUser:getUser?p1.00          sample           44.302           ms/op

Client.listUser                       sample   103394   9.272 ? 0.038   ms/op

Client.listUser:listUser?p0.00        sample            1.792           ms/op

Client.listUser:listUser?p0.50        sample            9.060           ms/op

Client.listUser:listUser?p0.90        sample           14.287           ms/op

Client.listUser:listUser?p0.95        sample           15.679           ms/op

Client.listUser:listUser?p0.99        sample           17.336           ms/op

Client.listUser:listUser?p0.999       sample           30.966           ms/op

Client.listUser:listUser?p0.9999      sample           38.161           ms/op

Client.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   Units

Client.createUser                      thrpt        3  44.810 ?  5.152  ops/ms

Client.existUser                       thrpt        3  53.153 ? 49.787  ops/ms

Client.getUser                         thrpt        3  41.754 ?  8.210  ops/ms

Client.listUser                        thrpt        3  14.791 ?  3.458  ops/ms

Client.createUser                       avgt        3   0.719 ?  0.080   ms/op

Client.existUser                        avgt        3   0.574 ?  0.434   ms/op

Client.getUser                          avgt        3   0.703 ?  0.045   ms/op

Client.listUser                         avgt        3   2.189 ?  0.353   ms/op

Client.createUser                     sample  1267915   0.756 ?  0.002   ms/op

Client.createUser:createUser?p0.00    sample            0.099            ms/op

Client.createUser:createUser?p0.50    sample            0.678            ms/op

Client.createUser:createUser?p0.90    sample            0.888            ms/op

Client.createUser:createUser?p0.95    sample            1.034            ms/op

Client.createUser:createUser?p0.99    sample            3.383            ms/op

Client.createUser:createUser?p0.999   sample           10.502            ms/op

Client.createUser:createUser?p0.9999  sample           22.650            ms/op

Client.createUser:createUser?p1.00    sample           35.979            ms/op

Client.existUser                      sample  1461428   0.656 ?  0.002   ms/op

Client.existUser:existUser?p0.00      sample            0.077            ms/op

Client.existUser:existUser?p0.50      sample            0.504            ms/op

Client.existUser:existUser?p0.90      sample            1.128            ms/op

Client.existUser:existUser?p0.95      sample            1.516            ms/op

Client.existUser:existUser?p0.99      sample            2.802            ms/op

Client.existUser:existUser?p0.999     sample            6.452            ms/op

Client.existUser:existUser?p0.9999    sample           33.358            ms/op

Client.existUser:existUser?p1.00      sample           58.262            ms/op

Client.getUser                        sample  1270938   0.755 ?  0.003   ms/op

Client.getUser:getUser?p0.00          sample            0.084            ms/op

Client.getUser:getUser?p0.50          sample            0.588            ms/op

Client.getUser:getUser?p0.90          sample            1.034            ms/op

Client.getUser:getUser?p0.95          sample            1.626            ms/op

Client.getUser:getUser?p0.99          sample            4.473            ms/op

Client.getUser:getUser?p0.999         sample           10.830            ms/op

Client.getUser:getUser?p0.9999        sample           27.719            ms/op

Client.getUser:getUser?p1.00          sample           45.875            ms/op

Client.listUser                       sample   442763   2.166 ?  0.009   ms/op

Client.listUser:listUser?p0.00        sample            0.306            ms/op

Client.listUser:listUser?p0.50        sample            1.767            ms/op

Client.listUser:listUser?p0.90        sample            3.039            ms/op

Client.listUser:listUser?p0.95        sample            4.415            ms/op

Client.listUser:listUser?p0.99        sample           11.551            ms/op

Client.listUser:listUser?p0.999       sample           21.045            ms/op

Client.listUser:listUser?p0.9999      sample           32.702            ms/op

Client.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   Units

Client.createUser                      thrpt        3  33.678 ? 2.656  ops/ms

Client.existUser                       thrpt        3  50.030 ? 3.509  ops/ms

Client.getUser                         thrpt        3  34.125 ? 4.886  ops/ms

Client.listUser                        thrpt        3  11.929 ? 1.746  ops/ms

Client.createUser                       avgt        3   0.955 ? 0.164   ms/op

Client.existUser                        avgt        3   0.642 ? 0.051   ms/op

Client.getUser                          avgt        3   0.940 ? 0.071   ms/op

Client.listUser                         avgt        3   2.603 ? 0.748   ms/op

Client.createUser                     sample   985106   0.973 ? 0.003   ms/op

Client.createUser:createUser?p0.00    sample            0.148           ms/op

Client.createUser:createUser?p0.50    sample            0.855           ms/op

Client.createUser:createUser?p0.90    sample            1.147           ms/op

Client.createUser:createUser?p0.95    sample            1.315           ms/op

Client.createUser:createUser?p0.99    sample            5.300           ms/op

Client.createUser:createUser?p0.999   sample           12.517           ms/op

Client.createUser:createUser?p0.9999  sample           21.037           ms/op

Client.createUser:createUser?p1.00    sample           31.850           ms/op

Client.existUser                      sample  1470527   0.652 ? 0.001   ms/op

Client.existUser:existUser?p0.00      sample            0.092           ms/op

Client.existUser:existUser?p0.50      sample            0.601           ms/op

Client.existUser:existUser?p0.90      sample            0.800           ms/op

Client.existUser:existUser?p0.95      sample            0.876           ms/op

Client.existUser:existUser?p0.99      sample            1.550           ms/op

Client.existUser:existUser?p0.999     sample            9.650           ms/op

Client.existUser:existUser?p0.9999    sample           14.844           ms/op

Client.existUser:existUser?p1.00      sample           30.573           ms/op

Client.getUser                        sample  1001893   0.957 ? 0.004   ms/op

Client.getUser:getUser?p0.00          sample            0.127           ms/op

Client.getUser:getUser?p0.50          sample            0.741           ms/op

Client.getUser:getUser?p0.90          sample            1.401           ms/op

Client.getUser:getUser?p0.95          sample            2.191           ms/op

Client.getUser:getUser?p0.99          sample            5.546           ms/op

Client.getUser:getUser?p0.999         sample           12.059           ms/op

Client.getUser:getUser?p0.9999        sample           24.518           ms/op

Client.getUser:getUser?p1.00          sample           49.873           ms/op

Client.listUser                       sample   363958   2.636 ? 0.013   ms/op

Client.listUser:listUser?p0.00        sample            0.390           ms/op

Client.listUser:listUser?p0.50        sample            2.071           ms/op

Client.listUser:listUser?p0.90        sample            4.084           ms/op

Client.listUser:listUser?p0.95        sample            6.947           ms/op

Client.listUser:listUser?p0.99        sample           12.403           ms/op

Client.listUser:listUser?p0.999       sample           22.940           ms/op

Client.listUser:listUser?p0.9999      sample           40.935           ms/op

Client.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)的请求,在多少毫秒内。

整理结果如下表:

java kryopt_性能测试 —— Dubbo 基准测试_第2张图片

三个测试用例,差别是序列化使用的库,所以序列化的性能,决定了整体的性能结果。

方法的性能排行是: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 等等。

你可能感兴趣的:(java,kryopt)