一、前言

  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、服务接口相关代码:

 package ibusiness;

 import java.util.List;

 import model.*;

 public interface IBusinessOrder {
public String SendStr(String str); public List<OrderInfo> LoadOrders(List<OrderInfo> orders); public OrderInfo LoadOrder(OrderInfo order);
}

  2、服务实现相关代码,测试数据在服务器端不做任何处理原样返回:

 package business;

 import ibusiness.IBusinessOrder;

 import java.util.List;

 import model.*;

 public class BusinessOrder implements IBusinessOrder {
public String SendStr(String str) {
return str;
} public List<OrderInfo> LoadOrders(List<OrderInfo> orders) {
return orders;
} public OrderInfo LoadOrder(OrderInfo order) {
return order;
}
}

五、单线程测试

  1、测试仅记录rpc调用时间,测试数据的读取组装以及首次建立连接等相关耗时时间不作统计,循环执行100次取平均值。  

  2、服务消费方测试代码

 import java.util.List;

 import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext; import com.alibaba.dubbo.rpc.service.EchoService;
import common.Common; import ibusiness.*;
import model.*; public class Program {
public static void main(String[] args) throws Exception { ApplicationContext ctx = new FileSystemXmlApplicationContext("src//applicationContext.xml");
IBusinessOrder orderBusiness = (IBusinessOrder) ctx.getBean("orderBusiness"); // EchoService echoService = (EchoService) orderBusiness;
// String status = echoService.$echo("OK").toString();
// if (!status.equals("OK")) {
// System.out.println("orderBusiness out of service!");
// return;
// } else {
// System.out.println("orderBusiness in service !");
// } long startMili, endMili;
int loop = 100; // 单个pojo
try {
OrderInfo order = Common.BuildOrder();
orderBusiness.LoadOrder(order); // 防止首次连接的开销 startMili = System.currentTimeMillis();
OrderInfo returnOrder = null;
for (int i = 0; i < loop; i++) {
returnOrder = orderBusiness.LoadOrder(order);
}
endMili = System.currentTimeMillis();
System.out.println("单个pojo 平均传输耗时为:" + ((endMili - startMili) / (float) loop) + "毫秒 ,返回对象BillNumber:" + returnOrder.getBillNumber());
} catch (Exception ex) {
System.out.println("单个pojo 测试失败!");
//ex.printStackTrace();
} // pojo集合 (100)
try {
List<OrderInfo> orderList = Common.BuildOrderList();
startMili = System.currentTimeMillis();
List<OrderInfo> returnOrderList = null;
for (int i = 0; i < loop; i++) {
returnOrderList = orderBusiness.LoadOrders(orderList);
}
endMili = System.currentTimeMillis();
System.out.println("pojo集合 (100) 平均传输耗时为:" + ((endMili - startMili) / (float) loop) + "毫秒 ,返回记录数:" + returnOrderList.size());
} catch (Exception ex) {
System.out.println("pojo集合 (100) 测试失败!");
} // 1K String
try {
String str1k = Common.Build1KString();
startMili = System.currentTimeMillis();
String returnStr1k = null;
for (int i = 0; i < loop; i++) {
returnStr1k = orderBusiness.SendStr(str1k);
}
endMili = System.currentTimeMillis();
System.out.println("1K String 平均传输耗时为:" + ((endMili - startMili) / (float) loop) + "毫秒,返回字符长度:" + returnStr1k.length());
} catch (Exception ex) {
System.out.println("1K String 测试失败!");
} // 100K String
try {
String str100K = Common.Build100KString();
startMili = System.currentTimeMillis();
String returnStr100k = null;
for (int i = 0; i < loop; i++) {
returnStr100k = orderBusiness.SendStr(str100K);
}
endMili = System.currentTimeMillis();
System.out.println("100K String 平均传输耗时为:" + ((endMili - startMili) / (float) loop) + "毫秒,返回字符长度:" + returnStr100k.length());
} catch (Exception ex) {
System.out.println("100K String 测试失败!");
} // 1M String
try {
String str1M = Common.Build1MString();
startMili = System.currentTimeMillis();
String returnStr1M = null;
for (int i = 0; i < loop; i++) {
returnStr1M = orderBusiness.SendStr(str1M);
}
endMili = System.currentTimeMillis();
System.out.println("1M String 平均传输耗时为:" + ((endMili - startMili) / (float) loop) + "毫秒,返回字符长度:" + returnStr1M.length());
} catch (Exception ex) {
System.out.println("1M String 测试失败!");
} System.out.println("all test done!");
}
}

  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协议下的默认序列化方式,推荐使用。

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

基于dubbo框架下的RPC通讯协议性能测试的更多相关文章

  1. 基于Dubbo框架构建分布式服务(一)

    Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...

  2. 基于Dubbo框架构建分布式服务

    Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...

  3. [转载] 基于Dubbo框架构建分布式服务

    转载自http://shiyanjun.cn/archives/1075.html Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务 ...

  4. 基于Laravel框架下使用守护进程supervisor实现定时任务(毫秒)

    本篇文章给大家带来的内容是关于基于Laravel框架下使用守护进程supervisor实现定时任务(毫秒),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 公司需要实现X分钟内每隔Y秒 ...

  5. 基于Dubbo框架构建分布式服务(集群容错&负载均衡)

    Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...

  6. 基于netty框架的轻量级RPC实现(附源码)

    前言 Rpc( Remote procedure call):是一种请求 - 响应协议.RPC由客户端启动,客户端向已知的远程服务器发送请求消息,以使用提供的参数执行指定的过程.远程服务器向客户端发送 ...

  7. 基于Dubbo框架构建分布式服务(二)

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> ...

  8. Tomcat服务器本地的搭建,以及在 IDEA软件下的配置,以及项目的测试运行(基于supermvc框架下的web)

    一.声明 使用了基于springmvc的supermvc的web框架.实习公司的框架. 二.tomact的下载与安装 1选择适合自己电脑配置的jdk和jre版本(截图来自tomcat的官方网站http ...

  9. 基于分布式思想下的RPC解决方案--笔记

    分布式: RPC可以提高系统稳定性,比如说,我们的订单服务程序更新出BUG,导致内存溢出,是这台服务器宕机了,但是它只会影响的整个系统的订单业务部分,对于用户注册登录等业务没有影响,同样对于系统的日志 ...

随机推荐

  1. Unicode文件读取 出现隐藏字符 (大坑)

    C#读取文件..分析时发现应该15位的.. str.Lenght 却 16位.. 字符串复制出来一位位的数..就是15位.. 纳闷中突然想起来会不会是隐藏字符.. 输出 str[0].ToBytes( ...

  2. rsync快速删除海量文件

    rsync --delete-before -avH --progress --stats /tmp/empty/ /var/spool/postfix/maildrop/ 由于业务侧使用时,一些脚本 ...

  3. JS中数组Array的用法{转载}

    js数组元素的添加和删除一直比较迷惑,今天终于找到详细说明的资料了,先给个我测试的代码^-^var arr = new Array();arr[0] = "aaa";arr[1] ...

  4. HDU 5686 斐波那契数列、Java求大数

    原题:http://acm.hdu.edu.cn/showproblem.php?pid=5686 当我们要求f[n]时,可以考虑为前n-1个1的情况有加了一个1. 此时有两种情况:当不适用第n个1进 ...

  5. Qml 写的弹出层控件

    QML弹出窗口组件,灯箱效果.动画效果,可拖拽 核心思路:一个mask层,一个最顶层,都用rectangle,禁止事件穿透 使用 Popup { id: popup width: 200; heigh ...

  6. centos6.5 lamp 环境 使用yum安装方法

    从网上找了一些 最后整理了下 1.安装Apache yum -y install httpd # 开机自启动 chkconfig httpd on # 启动httpd 服务 service httpd ...

  7. Git命令回顾

    团队从Eclipse迁移到Android Studio之后,也从SVN迁移到Git了. 一直忙于需求迭代无暇做迁移,现在才开始做,相见恨晚,好东西,高大上,词穷. 回顾和记录一下git的一些基本操作. ...

  8. java 跨域

    jsonp做前端跨域需要服务器的支持的,造成json字符串前缀 var a=...或者 a[].... 实在有点麻烦,故还是后台跨域取数据好了 package com.pro.domain; impo ...

  9. LVS集群之DR模式 实现

    ps:做 dr 模式 之前,先把之前做过的操作清空掉 1.ipvsadm -ln 查看规则 2.ipvsadm -C 清空规则 3.ipvsadm -ln 确认 4.iptables -t nat - ...

  10. 3. UILable 的使用

    1.  坐标系 来自:http://www.cnblogs.com/mcj-coding/p/5100455.html  QQ: 863740091 如果在平面坐标系中我们要确定一个东西的位置和大小需 ...