dubbo 是阿里巴巴公司开源的一个 java 高性能优秀的服务框架,使得应用可通过高性能的 rpc 实现服务的输出和输入功能,可以和 spring 框架无缝集成。
motan 是新浪微博开源的一个 java 框架。它诞生的比较晚,起于 2013 年,2016 年 5 月开源。motan 在微博平台中已经广泛应用,每天为数百个服务完成近千亿次的调用。
rpcx 是 go 语言生态圈的 dubbo, 比 dubbo 更轻量,实现了 dubbo 的许多特性,借助于 go 语言优秀的并发特性和简洁语法,可以使用较少的代码实现分布式的 rpc 服务。
grpc 是 google 开发的高性能、通用的开源 rpc 框架,其由 google 主要面向移动应用开发并基于 http/2 协议标准而设计,基于 protobuf(protocol buffers) 序列化协议开发,且支持众多开发语言。本身它不是分布式的,所以要实现上面的框架的功能需要进一步的开发。
thrift 是 apache 的一个跨语言的高性能的服务框架,也得到了广泛的应用。
以下是它们的功能比较:
对于 rpc 的考察, 性能是很重要的一点,因为 rpc 框架经常用在服务的大并发调用的环境中,性能的好坏决定服务的质量以及公司在硬件部署上的花费。
本文通过一个统一的服务,测试这四种框架实现的完整的服务器端和客户端的性能。
这个服务传递的消息体有一个 protobuf 文件定义:
syntax = "proto2";
package main;
option optimize_for = speed;
message benchmarkmessage {
required string field1 = 1;
optional string field9 = 9;
optional string field18 = 18;
optional bool field80 = 80 [default=false];
optional bool field81 = 81 [default=true];
required int32 field2 = 2;
required int32 field3 = 3;
optional int32 field280 = 280;
optional int32 field6 = 6 [default=0];
optional int64 field22 = 22;
optional string field4 = 4;
repeated fixed64 field5 = 5;
optional bool field59 = 59 [default=false];
optional string field7 = 7;
optional int32 field16 = 16;
optional int32 field130 = 130 [default=0];
optional bool field12 = 12 [default=true];
optional bool field17 = 17 [default=true];
optional bool field13 = 13 [default=true];
optional bool field14 = 14 [default=true];
optional int32 field104 = 104 [default=0];
optional int32 field100 = 100 [default=0];
optional int32 field101 = 101 [default=0];
optional string field102 = 102;
optional string field103 = 103;
optional int32 field29 = 29 [default=0];
optional bool field30 = 30 [default=false];
optional int32 field60 = 60 [default=-1];
optional int32 field271 = 271 [default=-1];
optional int32 field272 = 272 [default=-1];
optional int32 field150 = 150;
optional int32 field23 = 23 [default=0];
optional bool field24 = 24 [default=false];
optional int32 field25 = 25 [default=0];
optional bool field78 = 78;
optional int32 field67 = 67 [default=0];
optional int32 field68 = 68;
optional int32 field128 = 128 [default=0];
optional string field129 = 129 [default="xxxxxxxxxxxxxxxxxxxxx"];
optional int32 field131 = 131 [default=0];}
相应的 thrift 定义文件为
namespace java com.colobu.thriftstruct benchmarkmessage{ 1: string field1, 2: i32 field2, 3: i32 field3, 4: string field4, 5: i64 field5, 6: i32 field6, 7: string field7, 9: string field9, 12: bool field12, 13: bool field13, 14: bool field14, 16: i32 field16, 17: bool field17, 18: string field18, 22: i64 field22, 23: i32 field23, 24: bool field24, 25: i32 field25, 29: i32 field29, 30: bool field30, 59: bool field59, 60: i32 field60, 67: i32 field67, 68: i32 field68, 78: bool field78, 80: bool field80, 81: bool field81, 100: i32 field100, 101: i32 field101, 102: string field102, 103: string field103, 104: i32 field104, 128: i32 field128, 129: string field129, 130: i32 field130, 131: i32 field131, 150: i32 field150, 271: i32 field271, 272: i32 field272, 280: i32 field280,}service greeter { benchmarkmessage say(1:benchmarkmessage name);}
事实上这个文件摘自 grpc 项目的测试用例,使用反射为每个字段赋值,使用 protobuf 序列化后的大小为 581 个字节左右。因为 dubbo 和 motan 缺省不支持 protobuf,所以序列化和反序列化是在代码中手工实现的。
服务很简单:
service hello { // sends a greeting rpc say (benchmarkmessage) returns (benchmarkmessage) {}}
接收一个 benchmarkmessage,更改它前两个字段的值为"ok" 和 100,这样客户端得到服务的结果后能够根据结果判断服务是否正常的执行。
dubbo 的测试代码改自:
motan 的测试代码改自:
rpcx 和 grpc 的测试代码在:
thrift 使用 java 进行测试。
正如左耳朵耗子对 dubbo 批评一样,dubbo 官方的测试不正规 (性能测试应该怎么做?)。
本文测试将用吞吐率、相应时间平均值、响应时间中位数、响应时间最大值进行比较 (响应时间最小值都为 0,不必比较),当然最好以 top percentile 的指标进行比较,但是我没有找到 go 语言的很好的统计这个库,所以暂时比较中位数。
另外测试中服务的成功率都是 100%。
测试是在两台机器上执行的,一台机器做服务器,一台机器做客户端。
两台机器的配置都是一样的,比较老的服务器:
cpu: intel(r) xeon(r) cpu e5-2620 v2 @ 2.10ghz, 24 cores memory: 16g os: linux 2.6.32-358.el6.x86_64, centos 6.4 go: 1.7 java: 1.8 dubbo: 2.5.4-snapshot (2016-09-05) motan: 0.2.2-snapshot (2016-09-05) grpc: 1.0.0 rpcx: 2016-09-05 thrift: 0.9.3 (java)
分别在 client 并发数为 100、500、1000、2000 和 5000 的情况下测试,记录吞吐率 (每秒调用次数, throughput)、响应时间 (latency) 、成功率。
(更精确的测试还应该记录 cpu 使用率、内存使用、网络带宽、io 等,本文中未做比较)
首先看在四种并发下各 rpc 框架的吞吐率:
rpcx 的性能遥遥领先,并且其它三种框架在并发 client 很大的情况下吞吐率会下降。thrift 比 rpcx 性能差一点,但是还不错,远好于 grpc,dubbo 和 motan,但是随着 client 的增多,性能也下降的很厉害,在 client 较少的情况下吞吐率挺好。
在这四种并发的情况下平均响应:
这个和吞吐率的表现是一致的,还是 rpcx 最好,平均响应时间小于 30ms, dubbo 在并发 client 多的情况下响应时间很长。我们知道,在微服务流行的今天,一个单一的 rpc 的服务可能会被不同系统所调用,这些不同的系统会创建不同的 client。如果调用的系统很多,就有可能创建很多的 client。这里统计的是这些 client 总的吞吐率和总的平均时间。
平均响应时间可能掩盖一些真相,尤其是当响应时间的分布不是那么平均,所以我们还可以关注另外一个指标,就是中位数。这里的中位数指小于这个数值的测试数和大于这个数值的测试数相等。
grpc 框架的表现最好。
另外一个就是比较一下最长的响应时间,看看极端情况下各框架的表现:
rpcx 的最大响应时间都小于 1 秒,motan 的表现也不错,都小于 2 秒,其它两个框架表现不是太好。
本文以一个相同的测试 case 测试了四种 rpc 框架的性能,得到了这四种框架在不同的并发条件下的性能表现。期望读者能提出宝贵的意见,以便完善这个测试,并能增加更多的 rpc 框架的测试。