一、前言
Dubbo RPC服務(wù)框架支持豐富的傳輸協(xié)議、序列化方式等通訊相關(guān)的配置和擴展。dubbo執(zhí)行一次RPC請求的過程大致如下:消費者(Consumer)向注冊中心(Registry)執(zhí)行RPC請求,注冊中心分配服務(wù)URL并路由到具體服務(wù)提供方(Provider),消費者和服務(wù)提供方建立網(wǎng)絡(luò)連接,服務(wù)提供方在本地創(chuàng)建連接池對象并提供遠程服務(wù),對于長連接類型協(xié)議(如dubbo協(xié)議)將保持連接,減少握手認證,調(diào)用過程中可以避免頻繁建立和斷開連接導(dǎo)致的性能開銷,保持長連接需要有心跳包的發(fā)送,所以對于非頻繁調(diào)用的服務(wù)保持連接同樣會有消耗。更多關(guān)于dubbo詳細介紹請參照官方文檔( http://alibaba.github.io/dubbo-doc-static/Home-zh.htm )。
1、支持常見的傳輸協(xié)議:RMI、Dubbo、Hessain、WebService、Http等,其中Dubbo和RMI協(xié)議基于TCP實現(xiàn),Hessian和WebService基于HTTP實現(xiàn)。
2、傳輸框架:Netty、Mina、以及基于servlet等方式。
3、序列化方式:Hessian2、dubbo、JSON( fastjson 實現(xiàn))、JAVA、SOAP 等。
本文主要基于dubbo框架下的通訊協(xié)議進行性能測試對比。
?
二、測試方案
基于dubbo 2.5.3框架,使用zookeeper作為dubbo服務(wù)注冊中心,分別以單線程和多線程的方式測試以下方案:
| ? | Protocol | ? ? ? Transporter ? | ? ? Serialization ? ? | Remark |
| A | ?dubbo 協(xié)議 | ?netty | ?hessian2 | ? |
| B | ?dubbo 協(xié)議 | ?netty | ?dubbo | ? |
| C | ?dubbo 協(xié)議 | ?netty | ?java | ? |
| D | ?RMI?協(xié)議 | ?netty | ?java | ? |
| E | ?RMI?協(xié)議 | ?netty | ?hessian2 | ? |
| F | ?Hessian 協(xié)議 | ?servlet | ?hessian2 | ?Hessian,基于tomcat容器 ? ?? |
| G | ?WebService 協(xié)議 ? | ?servlet | ?SOAP | ?CXF,基于tomcat容器 ? |
?
三、傳輸測試數(shù)據(jù)
1、單POJO對象,嵌套復(fù)雜集合類型
2、POJO集合,包含100個單POJO對象
3、1K字符串
4、100K字符串
5、1M字符串?
?
四、服務(wù)接口和實現(xiàn)
1、服務(wù)接口相關(guān)代碼:?
1
package
ibusiness;
2
3
import
java.util.List;
4
5
import
model.*
;
6
7
public
interface
IBusinessOrder {
8
public
String SendStr(String str);
9
10
public
List<OrderInfo> LoadOrders(List<OrderInfo>
orders);
11
12
public
OrderInfo LoadOrder(OrderInfo order);
13
}
2、服務(wù)實現(xiàn)相關(guān)代碼,測試數(shù)據(jù)在服務(wù)器端不做任何處理原樣返回:
1
package
business;
2
3
import
ibusiness.IBusinessOrder;
4
5
import
java.util.List;
6
7
import
model.*
;
8
9
public
class
BusinessOrder
implements
IBusinessOrder {
10
public
String SendStr(String str) {
11
return
str;
12
}
13
14
public
List<OrderInfo> LoadOrders(List<OrderInfo>
orders) {
15
return
orders;
16
}
17
18
public
OrderInfo LoadOrder(OrderInfo order) {
19
return
order;
20
}
21
}
?
五、單線程測試
1、測試僅記錄rpc調(diào)用時間,測試數(shù)據(jù)的讀取組裝以及首次建立連接等相關(guān)耗時時間不作統(tǒng)計,循環(huán)執(zhí)行100次取平均值。
2、服務(wù)消費方測試代碼
1
import
java.util.List;
2
3
import
org.springframework.context.ApplicationContext;
4
import
org.springframework.context.support.FileSystemXmlApplicationContext;
5
6
import
com.alibaba.dubbo.rpc.service.EchoService;
7
import
common.Common;
8
9
import
ibusiness.*
;
10
import
model.*
;
11
12
public
class
Program {
13
public
static
void
main(String[] args)
throws
Exception {
14
15
ApplicationContext ctx =
new
FileSystemXmlApplicationContext("src//applicationContext.xml"
);
16
IBusinessOrder orderBusiness = (IBusinessOrder) ctx.getBean("orderBusiness"
);
17
18
//
EchoService echoService = (EchoService) orderBusiness;
19
//
String status = echoService.$echo("OK").toString();
20
//
if (!status.equals("OK")) {
21
//
System.out.println("orderBusiness out of service!");
22
//
return;
23
//
} else {
24
//
System.out.println("orderBusiness in service !");
25
//
}
26
27
long
startMili, endMili;
28
int
loop = 100
;
29
30
//
單個pojo
31
try
{
32
OrderInfo order =
Common.BuildOrder();
33
orderBusiness.LoadOrder(order);
//
防止首次連接的開銷
34
35
startMili =
System.currentTimeMillis();
36
OrderInfo returnOrder =
null
;
37
for
(
int
i = 0; i < loop; i++
) {
38
returnOrder =
orderBusiness.LoadOrder(order);
39
}
40
endMili =
System.currentTimeMillis();
41
System.out.println("單個pojo 平均傳輸耗時為:" + ((endMili - startMili) / (
float
) loop) + "毫秒 ,返回對象BillNumber:" +
returnOrder.getBillNumber());
42
}
catch
(Exception ex) {
43
System.out.println("單個pojo 測試失敗!"
);
44
//
ex.printStackTrace();
45
}
46
47
//
pojo集合 (100)
48
try
{
49
List<OrderInfo> orderList =
Common.BuildOrderList();
50
startMili =
System.currentTimeMillis();
51
List<OrderInfo> returnOrderList =
null
;
52
for
(
int
i = 0; i < loop; i++
) {
53
returnOrderList =
orderBusiness.LoadOrders(orderList);
54
}
55
endMili =
System.currentTimeMillis();
56
System.out.println("pojo集合 (100) 平均傳輸耗時為:" + ((endMili - startMili) / (
float
) loop) + "毫秒 ,返回記錄數(shù):" +
returnOrderList.size());
57
}
catch
(Exception ex) {
58
System.out.println("pojo集合 (100) 測試失敗!"
);
59
}
60
61
//
1K String
62
try
{
63
String str1k =
Common.Build1KString();
64
startMili =
System.currentTimeMillis();
65
String returnStr1k =
null
;
66
for
(
int
i = 0; i < loop; i++
) {
67
returnStr1k =
orderBusiness.SendStr(str1k);
68
}
69
endMili =
System.currentTimeMillis();
70
System.out.println("1K String 平均傳輸耗時為:" + ((endMili - startMili) / (
float
) loop) + "毫秒,返回字符長度:" +
returnStr1k.length());
71
}
catch
(Exception ex) {
72
System.out.println("1K String 測試失敗!"
);
73
}
74
75
//
100K String
76
try
{
77
String str100K =
Common.Build100KString();
78
startMili =
System.currentTimeMillis();
79
String returnStr100k =
null
;
80
for
(
int
i = 0; i < loop; i++
) {
81
returnStr100k =
orderBusiness.SendStr(str100K);
82
}
83
endMili =
System.currentTimeMillis();
84
System.out.println("100K String 平均傳輸耗時為:" + ((endMili - startMili) / (
float
) loop) + "毫秒,返回字符長度:" +
returnStr100k.length());
85
}
catch
(Exception ex) {
86
System.out.println("100K String 測試失敗!"
);
87
}
88
89
//
1M String
90
try
{
91
String str1M =
Common.Build1MString();
92
startMili =
System.currentTimeMillis();
93
String returnStr1M =
null
;
94
for
(
int
i = 0; i < loop; i++
) {
95
returnStr1M =
orderBusiness.SendStr(str1M);
96
}
97
endMili =
System.currentTimeMillis();
98
System.out.println("1M String 平均傳輸耗時為:" + ((endMili - startMili) / (
float
) loop) + "毫秒,返回字符長度:" +
returnStr1M.length());
99
}
catch
(Exception ex) {
100
System.out.println("1M String 測試失敗!"
);
101
}
102
103
System.out.println("all test done!"
);
104
}
105
}
3、測試數(shù)據(jù)耗時記錄
A、dubbo 協(xié)議、netty 傳輸、hessian2 序列化
<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="hessian2" ?/>
| 單個POJO | 0.958毫秒 |
| POJO集合?(100) | 1.438毫秒 |
| 1K String | 0.68毫秒 |
| 100K String | 4.262毫秒 |
| 1M String | 32.473毫秒? |
B、dubbo 協(xié)議、netty 傳輸、dubbo 序列化
| 單個POJO | 1.45毫秒 |
| POJO集合?(100) | 3.42毫秒 |
| 1K String | 0.94毫秒 |
| 100K String | 4.35毫秒 |
| 1M String | 27.92毫秒 |
C、dubbo 協(xié)議、netty 傳輸、java 序列化
<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="java" />?
| 單個POJO | 1.91毫秒 |
| POJO集合?(100) | 4.48毫秒 |
| 1K String | 1.0毫秒 |
| 100K String | 3.3毫秒 |
| 1M String | 18.09毫秒 |
D、RMI?協(xié)議、netty 傳輸、java 序列化?
<dubbo:protocol name="rmi" server="netty" port="1099" serialization="java" /> ??
| 單個POJO | 1.63毫秒 |
| POJO集合?(100) | 5.15毫秒 |
| 1K String | 0.77毫秒 |
| 100K String | 2.15毫秒 |
| 1M String | 15.21毫秒 |
E、RMI?協(xié)議、netty 傳輸、hessian2?序列化?
| 單個POJO | 1.63毫秒 |
| POJO集合?(100) | 5.12毫秒 |
| 1K String | 0.76毫秒 |
| 100K String | 2.13毫秒 |
| 1M String | 15.11毫秒 |
F、Hessian協(xié)議、servlet(tomcat容器)、hessian2?序列化?
| 單個POJO | 1.6毫秒 |
| POJO集合?(100) | 5.98毫秒 |
| 1K String | 1.88毫秒 |
| 100K String | 5.52毫秒 |
| 1M String | 39.87毫秒 |
G、WebService協(xié)議、servlet(tomcat容器)、SOAP序列化
<dubbo:protocol name="webservice" port="8080" server="servlet" />?
| 單個POJO | 7.4毫秒 |
| POJO集合?(100) | 34.39毫秒 |
| 1K String | 6.0毫秒 |
| 100K String | 7.43毫秒 |
| 1M String | 34.61毫秒 |
4、性能對比
?
六、多線程測試
1、由于測試機器配置較低,為了避免達到CPU瓶頸,測試設(shè)定服務(wù)消費方Consumer并發(fā)10個線程,每個線程連續(xù)對遠程方法執(zhí)行5次調(diào)用,服務(wù)提供方設(shè)置允許最大連接數(shù)100個,同時5個連接并行執(zhí)行,超時時間設(shè)置為5000ms,要求所有事務(wù)都能正確返回沒有異常,統(tǒng)計包含首次建立連接的消耗時間。
2、服務(wù)消費方測試代碼
?
3、測試數(shù)據(jù)耗時記錄
A、dubbo 協(xié)議、netty 傳輸、hessian2 序列化
| 單個POJO | 1165毫秒 |
| POJO集合?(100) | 1311毫秒 |
| 1K String | 1149毫秒 |
| 100K String | 1273毫秒 |
| 1M String | 2141毫秒 |
B、dubbo 協(xié)議、netty 傳輸、dubbo 序列化
<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="dubbo" />?
| 單個POJO | 1220毫秒 |
| POJO集合?(100) | 1437毫秒 |
| 1K String | 1145毫秒 |
| 100K String | 1253毫秒 |
| 1M String | 2065毫秒 |
C、dubbo 協(xié)議、netty 傳輸、java 序列化
<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="java" />?
| 單個POJO | 1188毫秒 |
| POJO集合?(100) | 1401毫秒 |
| 1K String | 1123毫秒 |
| 100K String | 1227毫秒 |
| 1M String | 1884毫秒 |
D、RMI?協(xié)議、netty 傳輸、java 序列化 ?
<dubbo:protocol name="rmi" server="netty" port="1099" serialization="java" /> ??
| 單個POJO | 1751毫秒 |
| POJO集合?(100) | 1569毫秒 |
| 1K String | 1766毫秒 |
| 100K String | 1356毫秒 |
| 1M String | 1741毫秒 |
E、RMI?協(xié)議、netty 傳輸、hessian2?序列化?
<dubbo:protocol name="rmi" server="netty" port="1099" serialization="hessian2" ?/>?
| 單個POJO | 1759毫秒 |
| POJO集合?(100) | 1968毫秒 |
| 1K String | 1239毫秒 |
| 100K String | 1339毫秒 |
| 1M String | 1736毫秒 |
F、Hessian協(xié)議、servlet、hessian2?序列化?
<dubbo:protocol name="hessian" port="8080" server="servlet" serialization="hessian2" />?
| 單個POJO | 1341毫秒 |
| POJO集合?(100) | 2223毫秒 |
| 1K String | 1800毫秒 |
| 100K String | 1916毫秒 |
| 1M String | 2445毫秒 |
G、WebService協(xié)議、servlet、SOAP序列化
<dubbo:protocol name="webservice" port="8080" server="servlet" />?
| 單個POJO | 1975毫秒 |
| POJO集合?(100) | 2768毫秒 |
| 1K String | 1894毫秒 |
| 100K String | 2098毫秒 |
| 1M String | 2887毫秒 |
4、性能對比
七、性能分析
測試過程中盡管考慮了非常多的影響因素,但仍然有很多局限性,包括連接數(shù)限制、并發(fā)量、線程池策略、Cache、IO、硬件性能瓶頸等等因素,而且各自的適用場景不同,測試結(jié)果 僅供參考 。
從單線程測試結(jié)果可以看出,dubbo協(xié)議采用NIO復(fù)用單一長連接更適合滿足高并發(fā)小數(shù)據(jù)量的rpc調(diào)用,而在大數(shù)據(jù)量下的傳輸性能并不好,建議使用rmi協(xié)議,多線程測試中dubbo協(xié)議對小數(shù)據(jù)量的rpc調(diào)用同樣保持優(yōu)勢,在大數(shù)據(jù)量的傳輸中由于長連接的原因?qū)Ρ萺mi協(xié)議傳輸耗時差距并不明顯,這點同樣驗證了上述觀點。關(guān)于數(shù)據(jù)的序列化方式選擇需要考慮序列化和反序列化的效率問題,傳輸內(nèi)容的大小,以及格式的兼容性約束,其中hessian2作為duobb協(xié)議下的默認序列化方式,推薦使用。
如果有描述錯誤或者不當?shù)牡胤綒g迎指正。
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

