dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型

概述

前段时间项目要作服务化,因此我比较了如今流行的几大RPC框架的优缺点以及使用场景,最终结合自己项目的实际状况选择了使用dubbox做为rpc基础服务框架。下面就简单介绍一下RPC框架技术选型的过程。html

RPC简述

该系列文章将讲述如下RPC框架的helloword实例以及其实现原理简述,因为每一种RPC框架的原理实现不一样且都比较复杂,若是想深刻研究还请自行到官网或者其余技术博客学习。 
RPC框架职责 
RPC框架要向调用方屏蔽各类复杂性,要向服务提供方也屏蔽各种复杂性:java

  • 调用方感受就像调用本地函数同样
  • 服务提供方感受就像实现一个本地函数同样来实现服务

RPC框架是架构(微)服务化的首要基础组件,它能大大下降架构微服务化的成本,提升调用方与服务提供方的研发效率,屏蔽跨进程调用函数(服务)的各种复杂细节 
RPC框架的职责是:让调用方感受就像调用本地函数同样调用远端函数、让服务提供方感受就像实现一个本地函数同样来实现服务git

对于不是很理解服务化的同窗能够看看这两篇文章: 
服务化架构的演进与实践 
浅谈服务化架构程序员

服务化的好处 
互联网架构为何要作服务化?github

服务架构的拆分原则

来源:李林峰的文章:华为内部如何实施微服务架构?基本就靠这5大原则算法

服务拆分原则:围绕业务功能进行垂直和水平拆分。大小粒度是难点,也是团队争论的焦点。spring

很差的实践

  • 以代码量做为衡量标准,例如500行之内。
  • 拆分的粒度越小越好,例如以单个资源的操做粒度为划分原则。

建议的原则

  • 功能完整性、职责单一性。
  • 粒度适中,团队可接受。
  • 迭代演进,非一蹴而就。
  • API的版本兼容性优先考虑。

代码量多少不能做为衡量微服务划分是否合理的原则,由于咱们知道一样一个服务,功能自己的复杂性不一样,代码量也不一样。还有一点须要重点强调,在项目刚开始的时候,不要指望微服务的划分一蹴而就。 
微服务架构的演进,应该是一个按部就班的过程。在一个公司、一个项目组,它也须要一个按部就班的演进过程。一开始划很差,没有关系。当演进到一个阶段时,微服务的部署、测试和运维等成本都很是低的时候,这对于你的团队来讲就是一个好的微服务。docker

服务架构的开发原则

微服务的开发还会面临依赖滞后的问题。例如:A要作一个身份证号码校验,依赖服务提供者B。因为B把身份证号码校验服务的开发优先级排的比较低,没法知足A的交付时间点。A会面临要么等待,要么本身实现一个身份证号码校验功能。数组

之前单体架构的时候,你们须要什么,每每喜欢本身写什么,这实际上是没有太严重的依赖问题。可是到了微服务时代,微服务是一个团队或者一个小组提供的,这个时候必定没有办法在某一个时刻同时把全部的服务都提供出来,“需求实现滞后”是必然存在的。缓存

一个好的实践策略就是接口先行,语言中立,服务提供者和消费者解耦,并行开发,提高产能。不管有多少个服务,首先须要把接口识别和定义出来,而后双方基于接口进行契约驱动开发,利用Mock服务提供者和消费者,互相解耦,并行开发,实现依赖解耦。

采用契约驱动开发,若是需求不稳定或者常常变化,就会面临一个接口契约频繁变动的问题。对于服务提供者,不能由于担忧接口变动而迟迟不对外提供接口,对于消费者要拥抱变动,而不是抱怨和抵触。要解决这个问题,一种比较好的实践就是管理 + 技术左右开弓:

  • 容许接口变动,可是对变动的频度要作严格管控。
  • 提供全在线的API文档服务(例如Swagger UI),将离线的API文档转成全在线、互动式的API文档服务。
  • API变动的主动通知机制,要让全部消费该API的消费者可以及时感知到API的变动。
  • 契约驱动测试,用于对兼容性作回归测试。

服务架构的测试原则

微服务开发完成以后须要对其进行测试。微服务的测试包括单元测试、接口测试、集成测试和行为测试等,其中最重要的就是契约测试:

利用微服务框架提供的Mock机制,能够分别生成模拟消费者的客户端测试桩和提供者的服务端测试桩,双方能够基于Mock测试桩对微服务的接口契约进行测试,双方都不须要等待对方功能代码开发完成,实现了并行开发和测试,提升了微服务的构建效率。基于接口的契约测试还能快速的发现不兼容的接口变动,例如修改字段类型、删除字段等。

服务架构的部署原则

测试完成以后,须要对微服务进行自动化部署。微服务的部署原则:独立部署和生命周期管理、基础设施自动化。须要有一套相似于CI/CD的流水线来作基础设施自动化,具体能够参考Netflix开源的微服务持续交付流水线Spinnaker: 

 

最后一块儿看下微服务的运行容器:微部署能够部署在Dorker容器、PaaS平台(VM)或者物理机上。使用Docker部署微服务会带来不少优先:

  • 一致的环境,线上线下环境一致。
  • 避免对特定云基础设施提供商的依赖。
  • 下降运维团队负担。
  • 高性能接近裸机性能。
  • 多租户。

相比于传统的物理机部署,微服务能够由PaaS平台实现微服务自动化部署和生命周期管理。除了部署和运维自动化,微服务云化以后还能够充分享受到更灵活的资源调度:

  • 云的弹性和敏捷。
  • 云的动态性和资源隔离。

服务架构的治理原则

服务部署上线以后,最重要的工做就是服务治理。微服务治理原则:线上治理、实时动态生效。 
微服务经常使用的治理策略:

  • 流量控制:动态、静态流控制。
  • 服务降级。
  • 超时控制。
  • 优先级调度。
  • 流量迁移。
  • 调用链跟踪和分析。
  • 服务路由。
  • 服务上线审批、下线通知。
  • SLA策略控制。
  • 微服务治理模型以下所示:

 

最上层是为服务治理的UI界面,提供在线、配置化的治理界面供运维人员使用。SDK层是提供了微服务治理的各类接口,供服务治理Portal调用。最下面的就是被治理的微服务集群,集群各节点会监听服务治理的操做去作实时刷新。例如:修改了流控阈值以后,服务治理服务会把新的流控的阈值刷到服务注册中心,服务提供者和消费者监听到阈值变动以后,获取新的阈值并刷新到内存中,实现实时生效。因为目前服务治理策略数据量不是特别大,因此能够将服务治理的数据放到服务注册中心(例如etcd/ZooKeeper),没有必要再单独作一套。

服务最佳实践

介绍完微服务实施以后,下面咱们一块儿学习下微服务的最佳实践。 
服务路由:本地短路策略。关键技术点:优先调用本JVM内部服务提供者,其次是相同主机或者VM的,最后是跨网络调用。经过本地短路,能够避免远程调用的网络开销,下降服务调用时延、提高成功率。原理以下所示:


服务调用方式:同步调用、异步调用、并行调用。一次服务调用,一般就意味着会挂一个服务调用线程。采用异步调用,能够避免线程阻塞,提高系统的吞吐量和可靠性。可是在实际项目中异步调用也有一些缺点,致使使用不是特别普遍: 
须要写异步回调逻辑,与传统的接口调用使用方式不一致,开发难度大一些。 
一些场景下须要缓存上下文信息,引入可靠性问题。 
并行调用适用于多个服务调用没有上下文依赖,逻辑上能够并行处理,相似JDK的Fork/Join, 并行服务调用涉及到同步转异步、异步转同步、结果汇聚等,技术实现难度较大,目前不少服务框架并不支持。采用并行服务调用,能够把传统串行的服务调用优化成并行处理,可以极大的缩短服务调用时延。

微服务故障隔离:线程级、进程级、容器级、VM级、物理机级等。关键技术点:

  • 支持服务部署到不一样线程/线程池中。
  • 核心服务和非核心服务隔离部署。
  • 为了防止线程膨胀,支持共享和独占两种线程池策略

 

谈到分布式,就绕不开事务一致性问题:大部分业务能够经过最终一致性来解决,极少部分须要采用强一致性。 

具体的策略以下:

  • 最终一致性,能够基于消息中间件实现。
  • 强一致性,使用TCC框架。服务框架自己不会直接提供“分布式事务”,每每根据实际须要迁入分布式事务框架来支持分布式事务。

微服务的性能三要素:

  • I/O模型,这个一般会选用非堵塞的,Java里面可能用java原生的。
  • 线程调度模型。
  • 序列化方式。

公司内部服务化,对性能要求较高的场景,建议使用异步非阻塞I/O(Netty) + 二进制序列化(Thrift压缩二进制等) + Reactor线程调度模型。 

最后咱们一块儿看下微服务的接口兼容性原则:技术保障、管理协同。

  • 制定并严格执行《微服务前向兼容性规范》,避免发生不兼容修改或者私自修改不通知周边的状况。
  • 接口兼容性技术保障:例如Thrift的IDL,支持新增、修改和删除字段、字段定义位置无关性,码流支持乱序等。
  • 持续交付流水线的每日构建和契约化驱动测试,可以快速识别和发现不兼容。

如今流行的RPC框架:

服务治理型

  • dubbo
  • dubbox
  • motan

多语言型

  • grpc
  • thrift
  • avro
  • Protocol Buffers (google) 

上图来自于dubbo。服务治理型RPC框架结构大多如此,大体分为服务提供者,服务消费者,注册中心,监控报警中心几大模块。

服务性能

在服务化,或者微服务化过程当中,首先考虑的问题就是性能问题,由于在服务化以后,会增长如下额外的性能开销:

  1. 客户端须要对消息进行序列化,主要占用CPU计算资源。
  2. 序列化时须要建立二进制数组,耗费JVM堆内存或者堆外内存。
  3. 客户端须要将序列化以后的二进制数组发送给服务端,占用网络带宽资源。
  4. 服务端读取到码流以后,须要将请求数据报反序列化成请求对象,占用CPU计算资源。
  5. 服务端经过反射的方式调用服务提供者实现类,反射自己对性能影响就比较大。
  6. 服务端将响应结果序列化,占用CPU计算资源。
  7. 服务端将应答码流发送给客户端,占用网络带宽资源。
  8. 客户端读取应答码流,反序列化成响应消息,占用CPU资源。

RPC框架高性能设计

要想提升效率,除了硬件的提高,主要考虑如下三个方面:

  1. I/O调度模型:同步阻塞I/O(BIO)仍是非阻塞I/O(NIO)。
  2. 序列化框架的选择:文本协议、二进制协议或压缩二进制协议。
  3. 线程调度模型:串行调度仍是并行调度,锁竞争仍是无锁化算法。

IO调度如今主流的就是netty。 
高性能序列化目前性能最好的是ice,google 的 pb协议,FB的thrift协议等 
线程没啥好说的,确定多线程了。固然也能够是AKKA(java)

总结

综上所述,服务化是如今大型互联网公司主流的架构模式,如今还有更流行的微服务,docker部署等等。

我的建议采用dubbox,集成其余各类协议,在该系列文章最后有各个协议的性能对比。

之因此建议采用dubbox是由于,dubbox有比价完善的服务治理模型,其包含ZK注册中心,服务监控等,能够很方便的为咱们服务。 
虽然dubbo自己不支持多语言,可是咱们能够集成其余的序列化协议,好比thrift、avro,使其能够支持多种入门语言,让部门间的协做沟通更加灵活方便

固然,在实际使用过程当中,尤为是集成其余协议的过程当中,确定须要对协议自己有比较深刻的了解,才能正确的使用。

motan

新浪微博开源的RPC框架

helloword示例直接去官网下载运行便可

github地址:https://github.com/weibocom/motan 
文档地址:https://github.com/weibocom/motan/wiki/zh_quickstart 
用户指南;https://github.com/weibocom/motan/wiki/zh_userguide

# grpc

中文版官方文档:gRPC 官方文档中文版

helloWord示例,我就是根据这个文章作的,写得挺详细的:rpc框架之gRPC 学习 - hello world

grpc原理: grpc原理分析

dubbo

dubbo 已经与12年年末中止维护升级,忽略

thrift

请参考我写的另外一篇文章:thrift学习笔记(一) thrift简介及第一个helloword程序

dubbox

dubbox 是当当团队基于dubbo升级的一个版本。是一个分布式的服务架构,可直接用于生产环境做为SOA服务框架。 
dubbo官网首页:http://dubbo.io/ 上面有详细的用户指南和官方文档,介绍的比较详细,这里再也不赘述。

当当官方的github地址:https://github.com/dangdangdotcom/dubbox

升级为spring4.X(及其余依赖组件)版本dubbox的github的地 
址:https://github.com/yjmyzz/dubbox

参考资料【博客:菩提树下的杨过 的文章写得很是全面,介绍的已经很是详细了】: 
dubbox升级spring到4.x及添加log4j2支持 
分布式服务框架 dubbo/dubbox 入门示例 
dubbox 的各类管理和监管 
dubbo/dubbox 增长原生thrift及avro支持

# 各个RPC框架性能比较

测试环境

jdk7 
win7 64位 
idea 
我的笔记本配置: 

person对象:

  1. private int age;
  2. private String name;
  3. private boolean sex;
  4. private int childrenCount;

测试数据,入参:

  1. private int ageStart;
  2. private int ageEnd;

返回值:

  1. Person.PersonBuilder builder = Person.builder();
  2. List<Person> list = new ArrayList<Person>();
  3. for (short i = 0; i < 10; i++) {
  4. list.add(builder.age(i).childrenCount(i).name("test" + i).sex(true).build());
  5. }
  6. return list;

各协议测试使用的配置

  • grpc


rpc getPersonList (yjmyzz.grpc.study.dto.QueryParameter) returns (yjmyzz.grpc.study.dto.PersonList) {}

  • motan

<motan:basicService export="demoMotan:8002" 
group="motan-demo-rpc" accessLog="false" shareChannel="true" module="motan-demo-rpc" 
application="myMotanDemo" registry="registry" id="serviceBasicConfig"/>

  • dubbox
  1. <dubbo:protocol name= "dubbo" serialization="kryo" optimizer="com.alibaba.dubbo.demo.SerializationOptimizerImpl"/>
  2.  
  3. <dubbo:service interface="com.alibaba.dubbo.demo.person.PersonService" ref="personService" protocol="dubbo"/>
  • thrift

TNonblockingServer + TFramedTransport

测试结果

  1.  
  2. rgpc 100000 次NettyServer调用,耗时:53102毫秒,平均1883次/秒 【简单grpc】
  3. rgpc 100000 次NettyServer调用,耗时:52138毫秒,平均1917次/秒 【简单grpc】
  4. rgpc 100000 次NettyServer调用,耗时:51800毫秒,平均1930次/秒 【简单grpc】
  5. rgpc 100000 次NettyServer调用,耗时:51313毫秒,平均1948次/秒 【简单grpc】
  6.  
  7. rgpc 100000 次NettyServer调用,耗时:56812毫秒,平均1760次/秒[2016-10-08 19:17:31] Dubbo service server started! 【dubbox.kryo】
  8. rgpc 100000 次NettyServer调用,耗时:55133毫秒,平均1813次/秒[2016-10-08 19:18:42] Dubbo service server started!【dubbox.kryo】
  9. rgpc 100000 次NettyServer调用,耗时:52280毫秒,平均1912次/秒[2016-10-08 19:20:01] Dubbo service server started! 【dubbox.kryo】
  10.  
  11. rgpc 100000 次NettyServer调用,耗时:44414毫秒,平均2251次/秒[2016-10-08 19:13:34] Dubbo service server started! 【dubbox.fst】
  12. rgpc 100000 次NettyServer调用,耗时:44805毫秒,平均2231次/秒[2016-10-08 19:12:25] Dubbo service server started! 【dubbox.fst】
  13. rgpc 100000 次NettyServer调用,耗时:46245毫秒,平均2162次/秒[2016-10-08 19:14:43] Dubbo service server started! 【dubbox.fst】
  14.  
  15. rgpc 100000 次NettyServer调用,耗时:12203毫秒,平均8194次/秒[2016-10-09 19:52:34] Dubbo service server started!【dubbox.thrift】
  16. rgpc 100000 次NettyServer调用,耗时:14142毫秒,平均7071次/秒[2016-10-09 19:30:17] Dubbo service server started!【dubbox.thrift】
  17. rgpc 100000 次NettyServer调用,耗时:13762毫秒,平均7266次/秒[2016-10-09 19:30:43] Dubbo service server started!【dubbox.thrift】
  18.  
  19.  
  20. rgpc 100000 次NettyServer调用,耗时:44334毫秒,平均2255次/秒 【motan】
  21. rgpc 100000 次NettyServer调用,耗时:37844毫秒,平均2642次/秒 【motan】
  22. rgpc 100000 次NettyServer调用,耗时:39007毫秒,平均2563次/秒 【motan】
  23. rgpc 100000 次NettyServer调用,耗时:38610毫秒,平均2590次/秒 【motan】

测试结果说明

  1. 使用的本身的笔记本电脑测试的,测试的方式可能不太专业,但可以说明问题。
  2. 经过上面结果能够看到,thrift的性能最好,并且是至关的好

网上其余人作的测试

ice-dubbo-thrift-grpc性能测试对比 
RPC框架的性能比较

总结

影响RPC性能的因素主要有:

  • 序列化性能
  • IO性能
  • 线程模式

序列化的话,确定是Google的PB协议和thrift最好,IO和线程的话,先流行的性能比较好的都是采用多线程非阻塞IO。

grpc是Google出品,使用了PB协议,可是因为它出现的比较晚,还不怎么成熟,并且采用http协议,很是适合如今的微服务,不过性能上差了许多,并且像服务治理与监控都须要额外的开发工做,因此放弃grpc。 
thrift和grpc同样,性能优越,可是开发难度相比较于dubbox和motan也是高了一点点,须要编写proto文件(其实对于程序员来讲这算不上难度)。像服务治理与监控也是须要额外的开发工做。 
dubbo比较老了,直接弃用。 
dubbox和后来的motan都比较适合咱们的团队。dubbox后来通过当当的开发,引入了rest风格的http协议,而且还支持kryo/fst/dubbo/thrift等其余协议,并且其余团队也在使用dubbo,集成方便,服务治理监控功能齐全,因此最终采用dubbox。

其实我我的而言仍是喜欢thrift,毕竟性嫩优越,在大型分布式系统中,哪怕一点点性能提高累计在一块儿也是很可观的。不过再具体选型的过程当中还要结合团队目前的情况和团队其余开发人员的接受程度进行取舍。

转自:https://blog.csdn.net/liubenlong007/article/details/54692241

相关文章
相关标签/搜索