基于dubbo框架下的RPC通讯协议性能测试

来源:互联网 发布:网络数字对讲机 编辑:程序博客网 时间:2024/06/05 01:59

一、前言

  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    RemarkA 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 }
复制代码

 

五、单线程测试

  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 }
复制代码

  3、测试数据耗时记录

A、dubbo 协议、netty 传输、hessian2 序列化

<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="hessian2"  />

单个POJO0.958毫秒POJO集合 (100)1.438毫秒1K String0.68毫秒100K String4.262毫秒1M String32.473毫秒 
 

B、dubbo 协议、netty 传输、dubbo 序列化

<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="dubbo" /> 
单个POJO1.45毫秒POJO集合 (100)3.42毫秒1K String0.94毫秒100K String4.35毫秒1M String27.92毫秒
 

C、dubbo 协议、netty 传输、java 序列化

<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="java" /> 

单个POJO1.91毫秒POJO集合 (100)4.48毫秒1K String1.0毫秒100K String3.3毫秒1M String18.09毫秒
 

D、RMI 协议、netty 传输、java 序列化 

<dubbo:protocol name="rmi" server="netty" port="1099" serialization="java" />   

单个POJO1.63毫秒POJO集合 (100)5.15毫秒1K String0.77毫秒100K String2.15毫秒1M String15.21毫秒
 

E、RMI 协议、netty 传输、hessian2 序列化 

<dubbo:protocol name="rmi" server="netty" port="1099" serialization="hessian2"  /> 
单个POJO1.63毫秒POJO集合 (100)5.12毫秒1K String0.76毫秒100K String2.13毫秒1M String15.11毫秒
 

F、Hessian协议、servlet(tomcat容器)、hessian2 序列化 

<dubbo:protocol name="hessian" port="8080" server="servlet" serialization="hessian2" /> 
单个POJO1.6毫秒POJO集合 (100)5.98毫秒1K String1.88毫秒100K String5.52毫秒1M String39.87毫秒
 

G、WebService协议、servlet(tomcat容器)、SOAP序列化

<dubbo:protocol name="webservice" port="8080" server="servlet" /> 

单个POJO7.4毫秒POJO集合 (100)34.39毫秒1K String6.0毫秒100K String7.43毫秒1M String34.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"  /> 
单个POJO1165毫秒POJO集合 (100)1311毫秒1K String1149毫秒100K String1273毫秒1M String2141毫秒
 

B、dubbo 协议、netty 传输、dubbo 序列化

<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="dubbo" /> 

单个POJO1220毫秒POJO集合 (100)1437毫秒1K String1145毫秒100K String1253毫秒1M String2065毫秒
 

C、dubbo 协议、netty 传输、java 序列化

<dubbo:protocol name="dubbo" server="netty" port="30001" serialization="java" /> 

单个POJO1188毫秒POJO集合 (100)1401毫秒1K String1123毫秒100K String1227毫秒1M String1884毫秒
 

D、RMI 协议、netty 传输、java 序列化 

<dubbo:protocol name="rmi" server="netty" port="1099" serialization="java" />   

单个POJO1751毫秒POJO集合 (100)1569毫秒1K String1766毫秒100K String1356毫秒1M String1741毫秒
 

E、RMI 协议、netty 传输、hessian2 序列化 

<dubbo:protocol name="rmi" server="netty" port="1099" serialization="hessian2"  /> 

单个POJO1759毫秒POJO集合 (100)1968毫秒1K String1239毫秒100K String1339毫秒1M String1736毫秒
 

F、Hessian协议、servlet、hessian2 序列化 

<dubbo:protocol name="hessian" port="8080" server="servlet" serialization="hessian2" /> 

单个POJO1341毫秒POJO集合 (100)2223毫秒1K String1800毫秒100K String1916毫秒1M String2445毫秒
 

G、WebService协议、servlet、SOAP序列化

<dubbo:protocol name="webservice" port="8080" server="servlet" /> 

单个POJO1975毫秒POJO集合 (100)2768毫秒1K String1894毫秒100K String2098毫秒1M String2887毫秒
 

  4、性能对比

 
 

七、性能分析

  测试过程中尽管考虑了非常多的影响因素,但仍然有很多局限性,包括连接数限制、并发量、线程池策略、Cache、IO、硬件性能瓶颈等等因素,而且各自的适用场景不同,测试结果仅供参考

  从单线程测试结果可以看出,dubbo协议采用NIO复用单一长连接更适合满足高并发小数据量的rpc调用,而在大数据量下的传输性能并不好,建议使用rmi协议,多线程测试中dubbo协议对小数据量的rpc调用同样保持优势,在大数据量的传输中由于长连接的原因对比rmi协议传输耗时差距并不明显,这点同样验证了上述观点。关于数据的序列化方式选择需要考虑序列化和反序列化的效率问题,传输内容的大小,以及格式的兼容性约束,其中hessian2作为duobb协议下的默认序列化方式,推荐使用。

  如果有描述错误或者不当的地方欢迎指正。

转载于:http://www.cnblogs.com/lengfo/p/4293399.html

0 0
原创粉丝点击