1、什么是RPC?
RPC(Remote Procedure Call),远程过程调用,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。通俗地讲,就是开发者能够像调用本地方法一样调用远程的服务。RPC的作用主要体现在两个方面:
- 屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法;
- 隐藏底层网络通信的复杂性,让我们更专注于业务逻辑。
两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样。比较知名开源的RPC框架有阿里的Dubbo、google的gRPC、Go语言的rpcx、Apache的thrift等。 要实现向调用本地服务一样调用远程方法可能需要涉及的知识有:
- 动态代理
- 反射
- 序列化、反序列化
- 网络通信
- 编解码
- NIO
- 服务发现和注册
- 心跳与链路检测
- 负载均衡
- ……
2、RPC 框架基本架构
从图上可以看出,RPC 框架主要由三部分构成:
- 注册中心,服务端在启动后,会将它提供的服务列表发布到注册中心;客户端向注册中心订阅服务地址。
- 客户端:通过本地代理模块 Proxy 调用服务端,Proxy 模块收到负责将方法、参数等数据编码转化成网络字节流;从服务列表中选取其中一个的服务地址,并将数据通过网络发送给服务端
- 服务端:接收到数据进行解码,得到请求信息;根据解码后的请求信息调用对应的服务,然后将调用结果返回给客户端;服务端和客户端的编解码是逆向的。
3、各组件的职责
3.1 一次 RPC 调用流程:
- 服务消费者(Client 客户端)通过本地调用的方式调用服务。
- 客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。
- 客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
- 服务端存根(Server Stub)收到消息后进行解码(反序列化操作)。
- 服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理
- 服务端(Server)本地服务业务处理。
- 处理结果返回给服务端存根(Server Stub)。
- 服务端存根(Server Stub)序列化结果。
- 服务端存根(Server Stub)将结果通过网络发送至消费方。
- 客户端存根(Client Stub)接收到消息,并进行解码(反序列化)。
- 服务消费方得到最终结果。
3.2 注册中心
注册中心的存在是为了更好更方便的管理应用中的每一个服务,是各个分布式节点之间的纽带。注册中心主要提供以下核心功能:
- 服务注册与发现:动态的增减服务节点,服务节点增减后动态的通知服务消费者,而不需要由消费者来更新配置。服务的实例列表发生变化(新增或者移除)时,通知订阅该服务的 Consumer,从而让 Consumer 能够刷新本地缓存。
- 服务配置:动态修改服务配置,并将其推送到服务提供者和服务消费者而不需要重启服务。
- 健康检查和服务摘除:主动的检查服务健康情况,对于宕机的服务将其摘除服务列表,Server超过一定时间未心跳时,从服务的实例列表移除.客户端长时间没有心跳也可以关闭连接。
3.2.1 CAP理论
CAP理论是分布式架构中重要理论
- 一致性(Consistency) (所有节点在同一时间具有相同的数据)
- 可用性(Availability) (保证每个请求不管成功或者失败都有响应)
- 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)
由于C与A的特性无法共存.CAP 不可能都取,只能取其中2个,要么AP要么CP
3.2.2 微服务架构中常见的注册中心
Zookeeper Zoopkeeper 在国内很长一段时间都是注册中心一哥.大部分是因为Dubbo 在国能的盛行.
Eureka Eureka是一家在线影片租赁提供商Netflix开源的, 这家公司的理念还是满超前的.
Nacos Nacos 是阿里开源的, 功能其实也很多, 服务注册, 配置管理, 动态 DNS 服务, 元数据管理
3.3 客户端Client
服务端其实包括了两个部分:一部分是接口定义或者叫契约,对客户端提供的其实只是提供的服务契约,让客户端知道请求和返回信息;另一部分是服务真正的实现。就跟我们在本地开发Service一样,会先定义Interface然后再写impl实现。服务端的做法其实和接口和实现分离十分类似。
客户端并不关心也不需要知道服务端的具体实现,只需要服务端对外暴露需要的接口规范。客户端要做的事情有:
- 实现RPC协议,把请求和响应正确的编解码并传输到服务端或者从服务端接收响应,实现序列化或者反序列化
- 实现服务发现,服务端必须知道真正要调用的服务的实现,当然这个逻辑需要通过动态代理来进行统一封装,这样才能在实际调用的时候实现无感知,一般契约和具体的实现会通过一个类似URL的唯一标识进行绑定
- 以及负载均衡,必须能够拉取到服务端信息,并通过一定的负载均衡策略选择服务端实例来提供服务,具体负载均衡策略可以参考【5】
3.4 服务端Server
Server端要实现的工作有:
- 加载服务,并缓存启动服务注册
- 对外暴露接口
- 本地调用具体实现
- 和客户端一样实现RPC协议,完成编解码序列化反序列化等工作 因此实现RPC一些这一部分一般都会单独做成一个模块,然后客户端和服务端就不用分别再去做这部分工作了。
4、简单对比 RPC 和 Restful API
面对对象不同:RPC 更侧重于动作。Restful 的主体是资源
RESTful 是面向资源的设计架构,但在系统中有很多对象不能抽象成资源,比如登录,修改密码等而 RPC 可以通过动作去操作资源。所以在操作的全面性上 RPC 大于 RESTful。
传输效率:RPC 效率更高。RPC,使用自定义的 TCP 协议,可以让请求报文体积更小,或者使用 HTTP2 协议,也可以很好的减少报文的体积,提高传输效率。复杂度:RPC 实现复杂,流程繁琐。REST 调用及测试都很方便。
RPC 实现需要实现编码,序列化,网络传输等。而 RESTful 不要关注这些,RESTful 实现更简单。灵活性:HTTP 相对更规范,更标准,更通用,无论哪种语言都支持 HTTP 协议。RPC 可以实现跨语言调用,但整体灵活性不如 RESTful。
总结:RPC 主要用于公司内部的服务调用,性能消耗低,传输效率高,实现复杂。、
HTTP 主要用于对外的异构环境,浏览器接口调用,App 接口调用,第三方接口调用等。
RPC 使用场景(大型的网站,内部子系统较多、接口非常多的情况下适合使用 RPC):
- 长链接。不必每次通信都要像 HTTP 一样去 3 次握手,减少了网络开销。
- 注册发布机制。RPC 框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。
- 安全性,没有暴露资源操作。
- 微服务支持。就是最近流行的服务化架构、服务化治理,RPC 框架是一个强力的支撑。
【参考】
【1】 如何手撸一个较为完整的RPC框架
【2】 RPC基本原理以及如何用Netty来实现RPC
【3】 花了一个星期,我终于把RPC框架整明白了!
【4】 谈一谈我所理解的微服务中的注册中心
【5】 rpc之负载均衡
本文已参与「新人创作礼」活动, 一起开启掘金创作之路。