RPC通信原理
基于网络的调用
问题:谁来解决这个跨进程调用的问题?
RPC:Remote Percedure Call 远程过程调用
定义了一台主机上的程序通过网络调用另外一台主机上的程序的子程序这一行为。
RPC符合CS模型,可以实现进程间的通信,许多技术框架都是基于这种概念而实现
RPC 的主要功能目标是让构建分布式计算(应用)更容易,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议规范,简单的来说就是像调用本地服务一样调用远程服务,对开发者而言是透明的。
为什么用RPC
1、分布式设计
2、部署灵活
3、解耦服务
4、扩展性强
常见RPC框架
1、Dubbo:阿里巴巴,java
2、gRPC:Google,多语言
3、Thrift:Facebook/apache,多语言
4、Spring Cloud:不仅仅是RPC,更多的是微服务架构下的一站式解决方案
RPC的优势
1、RPC框架一般使用长链接,不必每次通信都要3次握手,减少网络开销
2、RPC框架一般都有注册中心,有丰富的监控管理
3、发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作
4、协议私密,安全性较高
5、rpc 能做到协议更简单内容更小,效率更高
6、rpc是面向服务的更高级的抽象,支持服务注册发现,负载均衡,超时重试,熔断降级等高级特性
RPC架构图
RPC时序图
RPC流程图
RPC框架技术实现 要点:
- 注册中心
- 代理技术
- 序列化技术
- 网络通信协议
- 超时重试机制
- 路由负载策略
- 熔断限流
注册中心
作用:在高可用的生产环境中,服务一般都以集群方式提供服务,集群里面的IP等重要参数信息可能随时会发生变化,节点也可能会动态扩缩容,客户端需要能够及时感知服务端的变化,获取集群最新服务节点的连接信息,而这些变化要求是要对调用方应用无感知的
工具:
Zookeeper
Consul
Nacos
Eureka
代理技术
RPC的调用对用户来讲是透明的,内部核心技术采用的就是代理技术,RPC 会自动给接口生成一个代理实现,当我们在项目中注入接口的时候,运行过程中实际绑定的是这个接口生成的代理实现。在接口方法被调用的时候,它实际上是被生成代理类拦截到了,这样就可以在生成的代理类里面,加入其他调用处理逻辑
JDK动态代理:
在运行期动态的创建代理类,它是通过接口生成代理类的,与静态代理相比更加灵活,但是也有一定的限制,第一是代理对象必须实现一个接口,否则会报异常。第二是有性能问题,因为是通过反射来实现调用的,所以比正常的直接调用来得慢,并且通过生成类文件也会多消耗部分方法区空间,可能引起Full GC。
CGLib
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库。
其原理是动态生成一个要代理类的子类,子类重写要代理的类的所有
不是final的方法,在子类中采用方法拦截的技术拦截所有父类方法
的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快
序列化技术
在网络传输中,数据必须采用二进制形式, 所以在RPC调用过程中, 需要采用序列化技术,对入参和出参进行序列化与反序列化
解析效率–压缩率压缩后体积–扩展性兼容性
可读性可调试–跨语言–通用性
常见序列化技术框架
JDK原生序列化
1、JAVA语言本身提供,使用比较方便和简单
2、不支持跨语言处理,性能相对不是很好,序列化以后产生的数据相对较大
JSON
轻量级数据交换格式
1、可读性好,方便阅读和调试,多语言支持,序列化以后的字节码文件相对较大,效率相对
不高,但对比XML序列化后的字节流更小,在企业运用普遍,特别是对前端和三方提供api。
Hessian2
二进制
1、Hessian 是一个动态类型,二进制序列化,并且支持跨语言特性的序列化框架。
2、Hessian 性能上要比 JDK、JSON 序列化高效很多,并且生成的字节数也更小。
有非常好的兼容性和稳定性,所以 Hessian 更加适合作为 RPC 框架远程通信的序列化协议
Proto buf
开源,高效
1、Google 推出的开源序列库,它是一种轻便、高效的结构化数据存储格式,多语言支持。
2、速度快,压缩比高,体积小,序列化后体积相比 JSON、Hessian 小很多
3、消息格式的扩展、升级和兼容性都不错,可以做到向后兼容
网络
RPC通信协议
RPC 就是将这种编程方式引入网络世界的一种尝试。因此,客户端将进行在它看来是正常的过程调用。客户端会将其打包成网络消息并传送给服务器。服务器会将其解包,并在服务器端将其转回为过程调用。服务器端的过程调用。这个调用的结果将被打包,以便返回给客户端。
Dubbo
GET /static/waplib/moonshad.js?tt=1716215555915 HTTP/1.1
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: keep-alive
Cookie: BIDUPSID=41E57E8E94D8352B07AA861868BCE214; PSTM=1655133186; BAIDUID=125744A5AC431EAB38CA7E0B6D241CCB:FG
Host: wappass.baidu.com
Referer: https://www.baidu.com/s
Sec-Fetch-Dest: script
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
sec-ch-ua: “Not_A Brand”;v=“8”, “Chromium”;v=“120”, “Microsoft Edge”;v=“120”
sec-ch-ua-mobile: ?0
系统I/O
RPC的调用过程中涉及到网络IO的操作,一般来说网络IO往往会成为系统的瓶颈所在,而不管上层应用如何使用,底层都是基于操作系统的IO模型。
线程模型
同步 异步
异步如何实现?
常用的方式就是Future 方式,它是返回 Future 对象,通过GET方式获取结果;
或者采用入参为 Callback 对象的回调方式,处理结果。
超时
超时重试机制
时间轮算法
在时钟轮机制中,有时间槽和时钟轮的概念,时间槽就相当于时钟的刻度;而时钟轮就相当于指针跳动的一个周期,我们可以将每个任务放到对应的时间槽位上。
1、每个任务会按要求只扫描执行一次,能很好的解决CPU 浪费的问题
2、秒级轮,分钟轮,小时轮除了用于检测rpc调用是否超时,也可以将定时心跳的任务添加到时间轮中,当前时间的心跳执行完后再将下一秒的心跳任务添加到时间轮中,这样就能做到每秒的定时心跳
路由,负载均衡
负载均衡策略
RPC Server为了高可用,可用选择做集群,因此在RPC Client端调用时要使用相应的均衡策略,这属于客户端负载均衡
熔断,限流
熔断器如同电力过载保护器。它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试恢复调用操作
Hystrix
限流:
实际生产环境中,每个服务节点都可能由于访问量过大而引起一系列问题,就需要业务提供方能够进行自我保护,从而保证在高访问量、高并发的场景下,系统依然能够稳定,高效运行。
限流器的作用是用来限制其请求的速率,保护后台响应服务,以免服务过载导致服务不可用现象出现。
限流的方法主要包括计数器、滑动窗口、漏桶和 令牌桶 等算法
限流组件:
Sentinel
RPC 与 REST 的区别?
RPC 与 REST 最大的区别就在于 RPC 提供了更好的抽象,RPC 甚至将网络传输细节彻底隐藏了,而 REST 没有。具体来说,REST 至少要求用于提供 URL 以及请求参数,而 RPC 隐藏了与网络传输的相关实现细节。另一方面,RPC 可以基于任何网络通信协议,而 REST 通常基于 HTTP(或者 HTTPS)协议。RPC 调用者并不会关心具体的协议是:HTTP、TCP 还是其他任何自定义协议。
RPC架构设计需要考虑以下几个方面:
接口设计:RPC的基础是远程调用,因此接口设计是关键。接口应该设计清晰、简洁、易于理解,并且具有良好的扩展性和兼容性。
通信协议:RPC的通信协议需要支持高效的数据传输和序列化,同时也需要支持可靠性和安全性等方面的需求。常用的通信协议有HTTP、TCP、UDP等。
数据传输格式:RPC通信的数据需要进行序列化和反序列化。常见的序列化格式有JSON、Protobuf、Thrift等。选用合适的数据传输格式可以提高RPC的性能和扩展性。
负载均衡和容错处理:在分布式系统中,服务的负载均衡和容错处理是必不可少的。RPC架构需要考虑如何实现负载均衡和容错处理,例如使用负载均衡算法、使用备用服务等。
安全性和可靠性:在RPC架构中,数据的安全性和可靠性也是非常重要的。需要考虑如何保证数据传输的安全性和可靠性,例如使用加密协议、数据压缩等。
RPC架构设计需要综合考虑以上多个方面的需求,并根据实际场景进行选择和实现,以实现高效、可靠、安全、可扩展的RPC系统。
RPC可以应用于各种分布式系统中,例如:
微服务架构:微服务架构中的各个服务通常都是通过RPC进行通信,实现服务之间的远程调用和数据传输。
云计算:云计算中的各种服务也需要通过RPC进行通信,例如计算、存储、网络等服务。
分布式数据库:分布式数据库中的各个节点之间需要进行数据同步和通信,也可以通过RPC实现。
消息队列:消息队列中的生产者和消费者之间也可以通过RPC进行通信,实现高效的消息传输和处理。
RPC架构
RPC 架构中的客户端组件和服务器端组件形成了一种对称结构,它们各司其职,但又共同构成一个整体。为了加深理解,这里我再总结下前面提到的各个组件。
客户端组件与职责包括:
RpcClient,负责调用远程 API,这个过程会依赖于 RpcProxy 提供的代理实现
RpcProxy,远程 API 的代理实现,提供远程服务本地化访问的入口
RpcCaller,负责编码和发送调用请求到服务方并等待结果
RpcConnector,负责与服务端建立通信通道并发送请求到服务端
服务端组件与职责包括:
RpcServer,负责实现远程 API
RpcInvoker,负责调用服务端的具体实现并返回结果
RpcProcessor,负责对请求进行处理,高效控制调用过程
RpcAcceptor,负责接收客户方请求并返回请求结果
而客户端和服务器端所共有的组件包括:
RpcProtocol,负责网络传输协议的编码和解码
RpcChannel,负责建立和维护网络数据传输通道
这样,我们对一个典型 RPC 架构中的基本结构和组件就有了完整的了解。
RPC 是分布式系统中一项基础设施类的技术体系,但凡涉及服务与服务之间的交互就需要使用到 RPC 架构。