讲述dubbo的分层结构和执行流程,以及和Spring Cloud的区别。
前言
这篇文章应该是月初就应该写的,当时看Dubbo相关的文章时一直提到RPC,所以就把RPC这块的知识先补齐。对于Dubbo,我目前需要对其整体初步了解,所以不会涉及到源码的内容,实战内容其实也很少,主要是有一个整体的初步人数,等后续用到Dubbo时,我再对其深入学习,包括Spring Cloud也是一样。
这篇文章参考敖丙的《dubbo系列 -rpc、dubbo基础知识》,我只对其进行重新整理。
RPC
RPC基础知识
详见《RPC基础系列》文章
简单实现一个RPC框架
我们这是 Demo,目的是突出 RPC框架重点功能 - 实现远程调用。所以啥七七八八的都没,并且我用伪代码来展示,其实也就是删除了一些保护性和约束性的代码,因为看起来太多了不太直观,需要一堆 try-catch 啥的,因此我删减了一些,直击重点。
首先我们定义一个接口和一个简单实现。
public interface AobingService { String hello(String name); } public class AobingServiceImpl implements AobingService { public String hello(String name) { return "Yo man Hello,I am" + name; } }
然后我们再来实现服务提供者暴露服务的功能。
public class AobingRpcFramework { public static void export(Object service, int port) throws Exception { ServerSocket server = new ServerSocket(port); while(true) { Socket socket = server.accept(); new Thread(new Runnable() { //反序列化 ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); String methodName = input.read(); //读取方法名 Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); //参数类型 Object[] arguments = (Object[]) input.readObject(); //参数 Method method = service.getClass().getMethod(methodName, parameterTypes); //找到方法 Object result = method.invoke(service, arguments); //调用方法 // 返回结果 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeObject(result); }).start(); } } public static <T> T refer (Class<T> interfaceClass, String host, int port) throws Exception { return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { Socket socket = new Socket(host, port); //指定 provider 的 ip 和端口 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.write(method.getName()); //传方法名 output.writeObject(method.getParameterTypes()); //传参数类型 output.writeObject(arguments); //传参数值 ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); Object result = input.readObject(); //读取结果 return result; } }); } }
好了,这个 RPC 框架就这样好了,是不是很简单?就是调用者传递了方法名、参数类型和参数值,提供者接收到这样参数之后调用对于的方法返回结果就好了!这就是远程过程调用,我们来看看如何使用:
//服务提供者只需要暴露出接口 AobingService service = new AobingServiceImpl (); AobingRpcFramework.export(service, 2333); //服务调用者只需要设置依赖 AobingService service = AobingRpcFramework.refer(AobingService.class, "127.0.0.1", 2333); service.hello();
敖丙写的这个示例,简单明了:
- 生产者:会一直去消费socket中的数据,如果没有的话,就会一直阻塞住,当socket中有数据时,阻塞放开,获取方法名和参数信息,然后执行类中的方法,并将结果写入socket。
- 消费者:其实就是搞了一个代理,当调用service.hello()时,就会触发该代理,这个代理其实就是初始化socket,然后类名和方法写入socket,然后等待服务端返回数据,最后取出数据,然后直接返回。
- 说明:这里只是一个RPC框架,真正的RPC其实还包括数据序列化和反序列化的过程,然后生产者和消费者的交互,可以是HTTP,也可以是其它方式,不一定需要我们直接操作Socket。
Dubbo基础
Dubbo 简介
Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。简单的说,Dubbo 就是个服务框架,说白了就是个远程服务调用的分布式框架。
Dubbo 发展历史
Dubbo 是阿里巴巴 2011年开源的一个基于 Java 的 RPC 框架,中间沉寂了一段时间,不过其他一些企业还在用 Dubbo 并自己做了扩展,比如当当网的 Dubbox,还有网易考拉的 Dubbok。但是在 2017 年阿里巴巴又重启了对 Dubbo 维护。在 2017 年荣获了开源中国 2017 最受欢迎的中国开源软件 Top 3。在 2018 年和 Dubbox 进行了合并,并且进入 Apache 孵化器,在 2019 年毕业正式成为 Apache 顶级项目。
目前 Dubbo 社区主力维护的是 2.6.x 和 2.7.x 两大版本,2.6.x 版本主要是 bug 修复和少量功能增强为准,是稳定版本。而 2.7.x 是主要开发版本,更新和新增新的 feature 和优化,并且 2.7.5 版本的发布被 Dubbo 认为是里程碑式的版本发布。它实现了面向接口的代理 RPC 调用,并且可以配合 ZooKeeper 等组件实现服务注册和发现功能,并且拥有负载均衡、容错机制等。
Dubbo 总体架构
这幅图在上篇文章画过,简单解释一下:
- Consumer:需要调用远程服务的服务消费方
- Registry:注册中心
- Provider:服务提供方
- Container:服务运行的容器
- Monitor:监控中心
一些注意的点:
- 首先注册中心和监控中心是可选的,你可以不要监控,也不要注册中心,直接在配置文件里面写然后提供方和消费方直连。
- 就算注册中心和监控中心宕机了也不会影响到已经正常运行的提供者和消费者,因为消费者有本地缓存提供者的信息。
Dubbo 分层架构
总的而言 Dubbo 分为三层,如果每一层再细分下去,一共有十层:
大的三层分别为 Business(业务层)、RPC 层、Remoting,并且还分为 API 层和 SPI 层。而分 API 层和 SPI 层这是 Dubbo 成功的一点,采用微内核设计+SPI扩展,使得有特殊需求的接入方可以自定义扩展,做定制的二次开发。
SPI(Service Provider Interface),是 JDK 内置的一个服务发现机制,它使得接口和具体实现完全解耦。我们只声明接口,具体的实现类在配置中选择。
咱们再来看看每一层都是干嘛的:
- Service,业务层,就是咱们开发的业务逻辑层。
- Config,配置层,主要围绕 ServiceConfig 和 ReferenceConfig,初始化配置信息。
- Proxy,代理层,服务提供者还是消费者都会生成一个代理类,使得服务接口透明化,代理层做远程调用和返回结果。
- Register,注册层,封装了服务注册和发现。
- Cluster,路由和集群容错层,负责选取具体调用的节点,处理特殊的调用要求和负责远程调用失败的容错措施。
- Monitor,监控层,负责监控统计调用时间和次数。
- Portocol,远程调用层,主要是封装 RPC 调用,主要负责管理 Invoker,Invoker代表一个抽象封装了的执行体,之后再做详解。
- Exchange,信息交换层,用来封装请求响应模型,同步转异步。
- Transport,网络传输层,抽象了网络传输的统一接口,这样用户想用 Netty 就用 Netty,想用 Mina 就用 Mina。
- Serialize,序列化层,将数据序列化成二进制流,当然也做反序列化。
Dubbo 调用过程
生产者和消费者的调用过程如下:
- 生产者服务注册
- 启动privider,根据协议Protocol将需要暴露出去的接口封装成Invoker;
- 通过 Exporter 包装一下,将 Exporter 通过 Registry 注册到注册中心。
- 消费者调用流程
- 消费者启动会向注册中心拉取服务提供者的元信息;
- Proxy 持有一个 Invoker 对象,调用 invoke 之后需要通过 Cluster 先从 Directory 获取所有可调用的远程服务的 Invoker 列表,如果配置了某些路由规则,比如某个接口只能调用某个节点的那就再过滤一遍 Invoker 列表;
- 剩下的 Invoker 通过 LoadBalance 做负载均衡选取一个;
- 经过 Filter 做一些统计相关的事情;
- 通过 Client 做数据传输,比如用 Netty 来传输;
- 传输需要经过 Codec 接口做协议构造,再序列化
- 将请求发送给服务提供者
提炼一下:初始化服务拿到服务信息 -> 构造数据请求 -> 通过负载均衡选取机器节点 -> 处理统计信息 -> 序列化数据 -> 请求发送给下游
- 生产者处理请求
- 接受请求,反序列化数据;
- 将数据扔到线程池;
- 跟进请求数据,找到对应的Exporter;
- 通过Filter进行简单过滤;
- 通过Exporter找到对应的Invoker,执行具体的方法,返回数据;
- 将数据序列化之后,直接返回。
提炼一下:接受请求 -> 反序列化数据 -> 扔入线程池 -> 找到Exporter -> 过滤请求 -> 通过Invoker执行 -> 反序列化结果 -> 返回请求
Dubbo特点
- 远程通讯: 提供对多种基于长连接的 NIO 框架抽象封装(非阻塞 I/O 的通信方式,Mina/Netty/Grizzly),包括多种线程模型,序列化(Hessian2/ProtoBuf),以及“请求-响应”模式的信息交换方式。
- 集群容错: 提供基于接口方法的透明远程过程调用(RPC),包括多协议支持(自定义 RPC 协议),以及软负载均衡(Random/RoundRobin),失败容错(Failover/Failback),地址路由,动态配置等集群支持。
- 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
下面简单讲述一下Dubbo和Spring Cloud的区别,不过在讲述两者区别之前,我们先简单了解一下Spring Cloud。
Spring Cloud简述
Spring Cloud简介
Spring Cloud 基于 Spring Boot,为微服务体系开发中的架构问题,提供了一整套的解决方案——服务注册与发现,服务消费,服务保护与熔断,网关,分布式调用追踪,分布式配置管理等。
Spring Boot 是 Spring 的一套快速配置脚手架,使用默认大于配置的理念,用于快速开发单个微服务。
Spring Cloud 完整技术
核心功能:
- 分布式/版本化配置
- 服务注册和发现
- 路由
- 服务和服务之间的调用
- 负载均衡
- 断路器
- 分布式消息传递
Spring Cloud 组件架构
流程:
- 请求统一通过 API 网关(Zuul)来访问内部服务。
- 网关接收到请求后,从注册中心(Eureka)获取可用服务。
- 由 Ribbon 进行均衡负载后,分发到后端具体实例。
- 微服务之间通过 Feign 进行通信处理业务。
- Hystrix 负责处理服务超时熔断。
- Turbine 监控服务间的调用和熔断相关指标。
Dubbo vs Spring Cloud
Dubbo优缺点
优点:
- Dubbo 支持 RPC 调用,服务之间的调用性能会很好。
- 支持多种序列化协议,如 Hessian、HTTP、WebService。
- Dobbo Admin后台管理功能强大,提供了路由规则、动态配置、访问控制、权重调节、均衡负载等功能。
- 在国内影响力比较大,中文社区文档较为全面。
- 阿里重启维护。
缺点:
- Registry 严重依赖第三方组件(zookeeper 或者 redis),当这些组件出现问题时,服务调用很快就会中断。
- Dubbo 只支持 RPC 调用。使得服务提供方(抽象接口)与调用方在代码上产生了强依赖,服务提供者需要不断将包含抽象接口的 jar 包打包出来供消费者使用。一旦打包出现问题,就会导致服务调用出错,并且以后发布部署会成很大问题(太强的依赖关系)。
- 另外,以后要兼容 .NET Core 服务,Dubbo RPC 本身不支持跨语言(可以用跨语言 RPC 框架解决,比如 Thrift、gRPC(重复封装了),或者自己再包一层 REST 服务,提供跨平台的服务调用实现,但相对麻烦很多)
- Dubbo 只是实现了服务治理,其他微服务框架并未包含,如果需要使用,需要结合第三方框架实现(比如分布式配置用淘宝的 Diamond、服务跟踪用京东的 Hydra,但使用相对麻烦些),开发成本较高,且风险较大。
- 社区更新不及时(虽然最近在疯狂更新),但也难免阿里以后又不更新了,就尴尬了。
- 主要是国内公司使用,但阿里内部使用 HSF,相对于 Spring Cloud,企业应用会差一些。
Spring Cloud优缺点
优点:
- 有强大的 Spring 社区、Netflix 等公司支持,并且开源社区贡献非常活跃。
- 标准化的将微服务的成熟产品和框架结合一起,Spring Cloud 提供整套的微服务解决方案,开发成本较低,且风险较小。
- 基于 Spring Boot,具有简单配置、快速开发、轻松部署、方便测试的特点。
- 支持 REST 服务调用,相比于 RPC,更加轻量化和灵活(服务之间只依赖一纸契约,不存在代码级别的强依赖),有利于跨语言服务的实现,以及服务的发布部署。另外,结合 Swagger,也使得服务的文档一体化。
- 提供了 Docker 及 Kubernetes 微服务编排支持。
- 国内外企业应用非常多,经受了大公司的应用考验(比如 Netfilx 公司),以及强大的开源社区支持。
缺点:
- 支持 REST 服务调用,可能因为接口定义过轻,导致定义文档与实际实现不一致导致服务集成时的问题(可以使用统一文档和版本管理解决,比如 Swagger)。
- REST 服务调用性能会比 RPC 低一些(但也不是强绑定)
- Spring Cloud 整合了大量组件,相关文档比较复杂,需要针对性的进行阅读。
整体对比
Dubbo 专注 RPC 和服务治理,Spring Cloud 则是一个微服务架构生态。
网友观点
使用 Dubbo 构建的微服务架构就像组装电脑,各环节我们的选择自由度很高,但是最终结果很有可能因为一条内存质量不行就点不亮了,总是让人不怎么放心,但是如果你是一名高手,那这些都不是问题;而 Spring Cloud 就像品牌机,在 Spring Source 的整合下,做了大量的兼容性测试,保证了机器拥有更高的稳定性,但是如果要在使用非原装组件外的东西,就需要对其基础有足够的了解。
关于 Dubbo 和 Spring Cloud 的相关概念和对比,上面已经叙述的很清楚了,我个人比较倾向于 Spring Cloud,原因就是真正的微服务框架、提供整套的组件支持、使用简单方便、强大的社区支持等等,另外,因为考虑到 .NET/.NET Core 的兼容处理,RPC 并不能很好的实现跨语言(需要借助跨语言库,比如 gRPC、Thrift,但因为 Dubbo 本身就是“gRPC”,在 Dubbo 之上再包一层 gRPC,有点重复封装了),而 HTTP REST 本身就是支持跨语言实现,所以,Spring Cloud 这一点还是非常好的(Dubbox 也支持,但性能相比要差一些)。
但凡事无绝对,每件事物有好的地方也有不好的地方,总的来说,Dubbo 和 Spring Cloud 的主要不同体现在两个方面:服务调用方式不同和专注点不同(生态不同)。
后记
这篇文章,其实主要是对Dubbo有一个整体的认识,然后初步了解Dubbo和Spring Cloud两者的区别,因为网上经常会把它们两拿来比较。
整篇文章都是概念性的东西,但是对于我们理解Dubbo又非常重要,可以只能作为入门篇,我目前也是先对Java技术栈的技术,先整体初步了解,后续会选择几个方向再深入学习。