基於dubbo框架下的RPC通訊協議性能測試


一、前言

  Dubbo RPC服務框架支持豐富的傳輸協議、序列化方式等通訊相關的配置和擴展。dubbo執行一次RPC請求的過程大致如下:消費者(Consumer)向注冊中心(Registry)執行RPC請求,注冊中心分配服務URL並路由到具體服務提供方(Provider),消費者和服務提供方建立網絡連接,服務提供方在本地創建連接池對象並提供遠程服務,對於長連接類型協議(如dubbo協議)將保持連接,減少握手認證,調用過程中可以避免頻繁建立和斷開連接導致的性能開銷,保持長連接需要有心跳包的發送,所以對於非頻繁調用的服務保持連接同樣會有消耗。更多關於dubbo詳細介紹請參照官方文檔(http://alibaba.github.io/dubbo-doc-static/Home-zh.htm)。

  1、支持常見的傳輸協議:RMI、Dubbo、Hessain、WebService、Http等,其中Dubbo和RMI協議基於TCP實現,Hessian和WebService基於HTTP實現。

  2、傳輸框架:Netty、Mina、以及基於servlet等方式。

  3、序列化方式:Hessian2、dubbo、JSON(fastjson 實現)、JAVA、SOAP 等。

  本文主要基於dubbo框架下的通訊協議進行性能測試對比。

 

二、測試方案

  基於dubbo 2.5.3框架,使用zookeeper作為dubbo服務注冊中心,分別以單線程和多線程的方式測試以下方案:

  Protocol       Transporter       Serialization     Remark
A  dubbo 協議  netty  hessian2  
B  dubbo 協議  netty  dubbo  
C  dubbo 協議  netty  java  
D  RMI 協議  netty  java  
E  RMI 協議  netty  hessian2  
F  Hessian 協議  servlet  hessian2  Hessian,基於tomcat容器     
G  WebService 協議    servlet  SOAP  CXF,基於tomcat容器  

 

三、傳輸測試數據

1、單POJO對象,嵌套復雜集合類型

2、POJO集合,包含100個單POJO對象

3、1K字符串

4、100K字符串

5、1M字符串 

 

四、服務接口和實現

  1、服務接口相關代碼: 

 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、服務實現相關代碼,測試數據在服務器端不做任何處理原樣返回:

 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 }
View Code

 

五、單線程測試

  1、測試僅記錄rpc調用時間,測試數據的讀取組裝以及首次建立連接等相關耗時時間不作統計,循環執行100次取平均值。  

  2、服務消費方測試代碼

  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) + "毫秒 ,返回記錄數:" + 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 }
View Code

  3、測試數據耗時記錄

A、dubbo 協議、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 協議、netty 傳輸、dubbo 序列化

<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="dubbo" /> 
單個POJO 1.45毫秒
POJO集合 (100) 3.42毫秒
1K String 0.94毫秒
100K String 4.35毫秒
1M String 27.92毫秒
 

C、dubbo 協議、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 協議、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 協議、netty 傳輸、hessian2 序列化 

<dubbo:protocol name="rmi" server="netty" port="1099" serialization="hessian2"  /> 
單個POJO 1.63毫秒
POJO集合 (100) 5.12毫秒
1K String 0.76毫秒
100K String 2.13毫秒
1M String 15.11毫秒
 

F、Hessian協議、servlet(tomcat容器)、hessian2 序列化 

<dubbo:protocol name="hessian" port="8080" server="servlet" serialization="hessian2" /> 
單個POJO 1.6毫秒
POJO集合 (100) 5.98毫秒
1K String 1.88毫秒
100K String 5.52毫秒
1M String 39.87毫秒
 

G、WebService協議、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瓶頸,測試設定服務消費方Consumer並發10個線程,每個線程連續對遠程方法執行5次調用,服務提供方設置允許最大連接數100個,同時5個連接並行執行,超時時間設置為5000ms,要求所有事務都能正確返回沒有異常,統計包含首次建立連接的消耗時間。

  2、服務消費方測試代碼

 

  3、測試數據耗時記錄

A、dubbo 協議、netty 傳輸、hessian2 序列化

<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="hessian2"  /> 
單個POJO 1165毫秒
POJO集合 (100) 1311毫秒
1K String 1149毫秒
100K String 1273毫秒
1M String 2141毫秒
 

B、dubbo 協議、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 協議、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 協議、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 協議、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協議、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協議、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、性能對比

 
 

七、性能分析

  測試過程中盡管考慮了非常多的影響因素,但仍然有很多局限性,包括連接數限制、並發量、線程池策略、Cache、IO、硬件性能瓶頸等等因素,而且各自的適用場景不同,測試結果僅供參考

  從單線程測試結果可以看出,dubbo協議采用NIO復用單一長連接更適合滿足高並發小數據量的rpc調用,而在大數據量下的傳輸性能並不好,建議使用rmi協議,多線程測試中dubbo協議對小數據量的rpc調用同樣保持優勢,在大數據量的傳輸中由於長連接的原因對比rmi協議傳輸耗時差距並不明顯,這點同樣驗證了上述觀點。關於數據的序列化方式選擇需要考慮序列化和反序列化的效率問題,傳輸內容的大小,以及格式的兼容性約束,其中hessian2作為duobb協議下的默認序列化方式,推薦使用。

  如果有描述錯誤或者不當的地方歡迎指正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM