一:RPC框架设计思路:
实现rpc框架,首先考虑几点功能:
1、消费者和提供者解耦。
2、处理消费者和提供者之间的RPC网络请求;
3、服务注册(对于提供者而言),服务发现(对于消费者而言);
首先,消费者和提供者要解耦,那么就构建一个api的包,其中只包含需要调用的服务的接口,消费者和提供者同时引用这个jar包,这样就实现了解耦;
消费者发起RPC时,将需要调用的服务接口名、方法名、参数类型、参数值 经过序列化,通过网络发送至提供者,提供者从网络接收请求,并反序列化,拿到消费者发送的内容,根据这个内容调用实现类中的方法,并将返回值写回网络响应。
具体分析:
(1)封装RPC调用请求:服务接口名、方法名、参数类型、参数值 可封装成一个RequestObject;
(2)RPC请求到达提供方,如何找到服务实现类:RequestObject经序列化和网络传输到达提供者后,怎样找到具体的实现类:提供者启动时,将自身提供的服务注册到一个map中,key是服务接口名,value是服务实现类,之后接收到RPC请求,直接根据请求中的服务接口名,即可找到对应的实现类,这个过程就是服务提供方的【本地注册】,本地注册的作用就是在调用到达提供方后,根据服务接口找到具体的服务实现类;
(3)消费者如何确定发送RPC调用地址:消费者发起RPC调用时,怎么知道往哪里发送调用,即如何确定服务提供方的地址,肯定是需要一个地方记录服务对应的ip+端口,这个也就是注册中心的基本功能。
此时仅通过RPC框架就做不到了:因为框架会打成一个jar包,被消费者和提供者引用,消费者和提供者在实际启动中,肯定是在两个进程空间中,如果框架jar包中提供注册中心的功能,即使提供者注册了,只在提供者的进程空间内能拿到,而消费者肯定拿不到,这样的话消费者拿不到服务注册的内容,无效注册。
因此要引入中间件作为注册中心【注册中心注册】,例如zk/redis/nacos,这样提供者注册服务地址之后,消费者去注册中心取地址,然后往这个地址发送RPC请求,就能成功调用了。
(4)消费者如何优雅发起RPC:消费者声明一个服务接口类型的对象,例如服务接口名叫做HelloService,服务提供的方法名叫做sayHello(),那么消费者定义: HelloService service = new HelloService();然后调用service.sayHello(); 这个时候,框架应当根据这个接口创建一个代理对象,在调用这个sayHello方法时,代理对象实际上是向提供者发送RPC请求,这样即可实现优雅调用。
(5)注册中心及缓存机制:如果消费者每次rpc调用都要查注册中心,那么效率就太低了,因此消费者会有一个本地缓存,存储注册中心的注册信息副本,这样每次都去本地缓存拿注册信息,提高效率。那么如果注册中心信息变动了,需要用“订阅通知”模式保持缓存和注册中心信息一致。当服务下线的时候,注册中心也会在一定时间内将服务注册信息清除掉,这点是利用zk的临时节点或者redis的超时时间的机制。
二:RPC框架知识点:
RPC-远程过程调用,是分布式系统的基础组件之一
分为服务提供者和服务消费者(调用方)
一次完整的RPC调用过程:
- 调用方将请求参数序列化为二进制数据(字节流)
- 根据RPC协议编码,将二进制数据用TCP发送到服务提供方
- 提供方从TCP通道接收到二进制数据
- 调用RPC协议解码,获取二进制数据包(避免粘包/半包问题)
- 将数据包反序列化,得到参数对象
- 找到对应的实现类,完成真正的方法调用
- 提供方将执行结果序列化,通过上述类似的过程,返回给调用方
RPC工作流程的关键点在:
(1)RPC协议;(2)序列化和反序列化;(3)网络通信
详解以上三点:
(1)RPC协议本质上就是应用层协议,和Http协议在同一层级,约定了数据包经过TCP及以下层级的网络传输后,如何在应用层封装/解封数据,所以它需要解决粘包/半包问题。
(2)序列化框架也能决定数据包大小、并要考虑兼容性;
(3)网络通信则需要考虑高效的网络IO模型。
几种序列化协议:
JSON:空间较大
Hessian:紧凑,性能和体积都比较好
Protobuf(Protocal Buffer):序列化后体积相比JSON\Hessian还要小,兼容性也不错
优先选择Hessian和Protobuf
如何提高RPC框架的网络通信性能:
5种IO模型:同步阻塞(BIO)、同步非阻塞、IO多路复用(NIO)、信号驱动、异步IO
主要使用BIO和NIO:
BIO为每个网络连接IO创建一个线程,有以下两个缺点:
(1)系统能撑住的线程数量是有限的,因此socket连接数量是有限的,不适用高并发;
(2)有两处阻塞:服务端阻塞等待客户端发起socket连接,工作线程阻塞读取客户端socket数据.
NIO一个线程处理多个socket连接:
扩展点有:Reactor模型、Netty框架