dubbo、motan、rpcx、gRPC、thrift的性能比較php
Dubbo 是阿里巴巴公司開源的一個Java高性能優秀的服務框架,使得應用可經過高性能的 RPC 實現服務的輸出和輸入功能,能夠和 Spring框架無縫集成。不過,略有遺憾的是,聽說在淘寶內部,dubbo因爲跟淘寶另外一個相似的框架HSF(非開源)有競爭關係,致使dubbo團隊已經解散(參見http://www.oschina.net/news/55059/druid-1-0-9 中的評論),反到是噹噹網的擴展版本仍在持續發展,牆內開花牆外香。其它的一些知名電商如噹噹、京東、國美維護了本身的分支或者在dubbo的基礎開發,可是官方的庫缺少維護,相關的依賴類好比Spring,Netty仍是很老的版本(Spring 3.2.16.RELEASE, netty 3.2.5.Final),卻是有些網友寫了升級Spring和Netty的插件。html
Motan是新浪微博開源的一個Java 框架。它誕生的比較晚,起於2013年,2016年5月開源。Motan 在微博平臺中已經普遍應用,天天爲數百個服務完成近千億次的調用。java
rpcx是Go語言生態圈的Dubbo, 比Dubbo更輕量,實現了Dubbo的許多特性,藉助於Go語言優秀的併發特性和簡潔語法,可使用較少的代碼實現分佈式的RPC服務。git
gRPC是Google開發的高性能、通用的開源RPC框架,其由Google主要面向移動應用開發並基於HTTP/2協議標準而設計,基於ProtoBuf(Protocol Buffers)序列化協議開發,且支持衆多開發語言。自己它不是分佈式的,因此要實現上面的框架的功能須要進一步的開發。github
thrift是Apache的一個跨語言的高性能的服務框架,也獲得了普遍的應用。shell
後續還會增長更多的 RPC 框架的比較,敬請收藏本文網址apache
如下是它們的功能比較:編程
Dubbo | Montan | rpcx | gRPC | Thrift | |
---|---|---|---|---|---|
開發語言 | Java | Java | Go | 跨語言 | 跨語言 |
分佈式(服務治理) | √ | √ | √ | × | × |
多序列化框架支持 | √ |
√ (當前支持Hessian二、Json,可擴展) |
√ |
× (只支持protobuf) |
× (thrift格式) |
多種註冊中心 | √ | √ | √ | × | × |
管理中心 | √ | √ | √ | × | × |
跨編程語言 | × | × (支持php client和C server) | × | √ | √ |
對於RPC的考察, 性能是很重要的一點,由於RPC框架常常用在服務的大併發調用的環境中,性能的好壞決定服務的質量以及公司在硬件部署上的花費。服務器
本文經過一個統一的服務,測試這四種框架實現的完整的服務器端和客戶端的性能。
這個服務傳遞的消息體有一個protobuf文件定義:網絡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
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定義文件爲
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
namespace java com.colobu.thrift
struct 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,因此序列化和反序列化是在代碼中手工實現的。
服務很簡單:
1
2
3
4
|
service Hello {
// Sends a greeting
rpc Say (BenchmarkMessage) returns (BenchmarkMessage) {}
}
|
接收一個BenchmarkMessage,更改它前兩個字段的值爲"OK" 和 100,這樣客戶端獲得服務的結果後可以根據結果判斷服務是否正常的執行。
Dubbo的測試代碼改自 dubo-demo,
Motan的測試代碼改自 motan-demo。
rpcx和gRPC的測試代碼在 rpcx benchmark。
Thrift使用Java進行測試。
正如左耳朵耗子對Dubbo批評同樣,Dubbo官方的測試不正規 (性能測試應該怎麼作?)。
本文測試將用吞吐率、相應時間平均值、響應時間中位數、響應時間最大值進行比較(響應時間最小值都爲0,沒必要比較),固然最好以Top Percentile的指標進行比較,可是我沒有找到Go語言的很好的統計這個庫,因此暫時比較中位數。
另外測試中服務的成功率都是100%。
測試是在兩臺機器上執行的,一臺機器作服務器,一臺機器作客戶端。
兩臺機器的配置都是同樣的,比較老的服務器:
分別在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框架的測試。
【轉自】http://colobu.com/2016/09/05/benchmarks-of-popular-rpc-frameworks/