前言:这篇文章,我们来说说RPC框架。为什么要讲RPC?首先,从个人成长的角度来说,新时代的码农如果能够清晰的理解RPC框架的要素,掌握服务注册发现、负载均衡、序列化协议、RPC通信协议、Socket通信、异步调用、熔丝降级等技术,全面提升基础品质。其次,目前市面上有很多优秀的框架,GitHub上也有相关的源码,但是好记性不如烂笔头。只有当我们真正理解并尝试编写一个RPC框架时,才是我们掌握这项技术的最好途径。.1.介绍和研究一个概念或框架,用三个W来考虑,你可能会对它有更深的理解:1)什么,什么是RPC框架,RPC是RemoteProcedureCall的缩写,远程过程调用,所以什么叫远程过程调用,你可以理解为我们调用外部(远程)服务就像调用我们自己的本地方法一样。2)Where,RPC框架用在什么地方?在分布式系统和微服务盛行的今天,每个业务系统都会独立拆分成一个个的Web应用,应用之间的交互和数据传输将成为必不可少的一环,RPC是独立服务之间远程交互的框架。3)为什么,为什么我们需要一个RPC框架?服务之间的调用需要考虑各种场景和因素。内部原理非常复杂繁琐。同时,在集群的情况下,服务负载均衡、熔断、限流都是需要的。考虑一下,这时候就需要一个集成了服务注册发现、负载均衡、序列化协议、RPC通信协议、Socket通信、异步调用、熔断降级等技术的技术来完成这些公共功能。RPC框架出现了,这种情况应运而生。目前主流的RPC框架有谷歌开源的GRPC、阿里巴巴的Dubbo、Netflix的SpringCloud。2、RPC框架基本上由RPC框架所需要的三个最基本的元素组成:ServiceProvider:提供相关服务接口的服务提供者。ServiceConsumer:服务消费者,消费者服务提供者的接口。Registry:注册中心,用于服务注册、发现、治理和高可用。在最基本的三个要素的基础上,还将扩展包括负载均衡器、断路器、通信协议组件、序列化协议等组件。最简单的RPC调用模型图如下:下面是一些术语的介绍和解释:2.1RegistryRegistry是RPC框架中的管理者和协调者角色,虽然服务消费者不会通过远程过程调用注册中心会直接向服务提供者发送请求,但是随着我们服务提供者的增多,每个服务的实例也在不断变化,需要将每个服务的地址、端口等信息通知给消费者是的,所以我们需要一个类似于“管家”的角色来负责管理服务的注册和发现。这个“管家”我们称之为注册中心。一个合格的注册中心需要具备缓存和持久化服务提供者数据、动态更新服务提供者信息、动态监控服务提供者节点变化、向消费者推送节点变化、查询服务提供者数据等功能。目前市面上比较流行的注册中心有:Zookeeper、Nacos、Consul、Eurake等,以上功能的实现方式也各不相同。下面是注册中心的对比:ZookeeperNacosConsulEurake一致性协议CPCP+APCPAP雪崩保护多数据中心不支持支持支持自动注销实例支持支持支持支持2.2服务提供者(RPC服务器)需要对外提供服务接口,一个服务provider需要包含启动连接注册中心,向注册中心注册相关信息,提供服务离线和更新机制,维护服务名称和服务映射,序列化和反序列化,启动通信等。目前服务提供者有两个维度服务提供,基于接口的服务提供和基于服务的服务提供,在Dubbo3.0之前,服务注册是基于接口维度的。Dubbo3.0之后逐渐向服务维度的服务注册和发现靠拢。SpringCloud基于服务进行注册和发现。在云原生和容器化越来越流行的今天,服务可以更好的适配容器化和云原生。2.3服务消费者(RPC消费者)服务消费者需要具备从注册中心拉取服务列表,缓存服务列表,动态监控和更新服务列表的功能,还需要有针对服务的负载均衡策略,序列化和反序列化,按照约定的通信协议进行调用等。目前Dubbo是基于proxy和Spring的BeanDefination实现的。消费开始时,会根据自定义的注解或配置扫描信息,然后生成对应的代理对象,注册到Spring容器中,调用时直接通过代理类进行一系列相关调用。SpringCloud是基于HTTP协议和增强版RestTemplate实现的。其内部实现了Ribbon负载均衡,消费者可以通过Feign或者RestTemplate实现远程调用。2.4通信框架通信框架是服务间IO交互和传输的保障。消费者需要通过通信框架与提供者交互,获取数据。目前市场上主流的基于Java的NIO通信框架是Netty。2.5通信协议通信协议是消费者与服务器之间约定的交互协议。消费者接收到服务端提供的IO流后,需要根据通信协议获取具体的数据内容。目前TCP通信协议比较常用的有HTTP、FTP、SMTP等协议。SpringCloud是基于HTTP通信协议实现的,Dubbo内部集成了一套通信协议。业界主流协议的解决方案可以归纳为:消息长度固定,比如每条消息的大小固定为100字节,不够就用空格补齐。在数据包末尾的特殊终止符处拆分。消息分为消息头和消息体,消息头中包含一个字段,表示消息的总长度(或消息体的长度)。通过对比,我们发现第三点最灵活,扩展性最好,一般推荐使用第三点。2.6序列化2.6.1概念介绍序列化是将对象序列化为二进制形式(字节数组)。一般序列化也称为编码(Encode),主要用于网络传输和数据持久化。反序列化(deserialization)就是把从网络、磁盘等读取的字节数组还原出来,这样就可以进行远程调用了。2.6.2序列化协议XML&SOAPXML是一种常用的序列化和反序列化协议,具有跨机器、跨语言的优点。XML有着悠久的历史。它的1.0版本早在1998年就形成了标准,一直被广泛使用至今。目前,它广泛应用于金融和银行业。JSONJSON(JavascriptObjectNotation)的全称源于弱类型语言Javascript,它的产生源于一个叫做“关联数组”的概念。其实质是用“属性-值”的方式来描述对象。其实在Javascript、PHP等弱类型语言中,一个类的描述方法就是一个关联数组。JSON具有数据简单、接受度高、结构简洁、序列化包小等特点。目前大部分公司都是采用这种序列化协议来实现的。Protobuf是Google开发的高性能序列化框架。它是一种纯显示层协议,可以与各种传输层协议一起使用。目前支持Java、C++、Python等语言。它具有数据量更小,解析速度更快,调用简单等特点,目前得物使用的Dubbo2.7.7版本默认使用该序列化协议。2.7负载均衡负载均衡是保证服务提供者在多实例情况下保证负载均衡的一种策略。目前一般有以下几种负载均衡策略:1)轮训,使用计数器,根据计数器的值和实例的数量进行减法。2)随机,采用随机请求的方式,随机化一个Random的值,按照random取余。3)Weightedround-robintraining,使用权重,为每个实例配置不同的权重比例,通过比例选择合适的实例。4)ConsistentHash,采用Hash环的方法,大致的实现思路是通过寻址找到最近的节点,可以上网搜索相关文档了解。3.实现既然是作为实践来实现一个RPC框架,我们就尽量借鉴目前主流的框架。在技??术选择上:注册中心:选择Zookeeper来实现。服务提供者:选择基于服务维度实现服务发现。目前的主流框架都是基于此。服务消费者:选择Dubbo类似代理的基于Spring的BeanDefination来实现。通信框架:Netty。通讯协议:使用上面的第三种方法。序列化协议:支持JSON和Protobuf。负载均衡:实现轮询和随机。当然,上述组件都是使用SpringBoot的SPI方式进行选择的。如果后面需要根据不同的配置进行扩展加载,可以通过配置实现插件的可插拔性。废话不多说,先贴一下项目的整体结构:整体代码结构如上,下面对各个模块一一进行说明:3.1注册中心代码实现如下:因为我们实现在一个可扩展的和接口方式,我们定义一些接口,通过不同的实现来区分,后面可以通过不同的配置来选择合适的注册中心。3.2服务提供者服务提供者使用Spring事件监听,同时根据声明式注解解析注册,通过组装元数据将自己的服务注册到注册中心完成服务提供者的服务注册,并启动Netty客户端同时监听客户端并调用服务。具体流程图如下:部分实现代码如下:自动设备逻辑:使用和服务注册逻辑:3.3服务消费者服务消费者的逻辑稍微复杂一些,需要通过自动创建一个新的BeanDefinationassembly,然后从所有的bean中找到带有RPC注解的相关参数,重写BeanDefination,重写后的Bean需要构建新的元数据存储在客户端缓存中,然后通过动态代理,创建代理对象。对象在服务发现时使用负载均衡选择地址,通过Netty与数据进行通信交互,最终返回相关的数据对象。具体过程如下:部分实现代码如下:启动类代码:代理类实现代码:负载均衡实现代码:(SPI可扩展模式)目前采用循环随机方式实现,可后面根据接口进行扩展。3.4通讯框架我们使用Netty4来实现通讯框架,使用NettyClient和NettyServer来实现:3.5通讯协议我们目前使用自定义的通讯协议来实现:第一个字节是一个幻数,比如我定义为0X35。第二个字节代表协议版本号,这样协议就可以扩展到使用不同的协议解析器。第三个字节是请求类型,比如0表示请求,1表示响应。第四个字节表示消息的长度,即四个字节之后这个长度的内容就是消息内容。3.6序列化协议目前支持JSON和ProtoBuf,后期我们可以通过SPI进行扩展配置。整体核心调用流程:4.总结本文从总体术语介绍和内部组件介绍两个方面阐述了RPC框架模型,从技术选型和具体代码实现了一个RPC框架并应用于项目。目前RPC框架已经整体搭建完成,可以通过注解的方式供业务方使用。但是,还有很多细节需要考虑和更仔细地考虑,比如系统的可扩展性和框架的整体性能。希望通过阅读本文,读者能够对RPC有更清晰的认识,提高自己的基本技能。*文/韦德关注得物技术,每周一、三、五晚18:30更新技术干货。如果觉得文章对你有帮助,欢迎评论转发点赞~
