RPC简介
RPC(Remote Procedure Call):远程过程调用,它是一种通过网路从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。
RPC是一种技术思想而非一种规范或协议,常见的RPC技术和框架有:
- 应用级的服务框架:Dubbo,Spring Boot/Spring Cloud,Google gRPC
- 远程通信协议:RMI,Socket,REST(HTTP JSON)
- 通信框架:Netty和MINA
完整的RPC框架
其中“RPC协议”就指明了程序如何进行网络传输和序列化。
RPC核心功能
一个RPC的核心功能由5个部分组成,分别是:客户端、客户端stub、网络传输模块、服务端stub、服务端。
- 客户端(Client):服务调用方;
- 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端;
- 网络传输模块(Network Service):底层传输,可以是TCP或HTTP
- 服务端存根(Server Stub):接收客户端发送过来的请求并进行解包,然后再调用本地服务进行处理;
- 服务端(Server):服务的真正提供者
一次RPC调用流程如下:
- 服务消费者(Client客户端)通过本地调用的方式调用服务;
- 客户端存根(Client Stub)接收到调用请求后负责将方法,入参等信息序列化成能够进行网络传输的消息体;
- 客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端;
- 服务端存根(Server Stub)接收到消息后进行反序列化操作
- 服务端存根(Server Stub)根据结果调用本地的服务进行相关处理;
- 服务端(Server)进行本地服务业务处理
- 处理结果返回给服务端存根
- 服务端存根序列化结果
- 服务端存根将结果通过网络发送至消费者
- 客户端存根接收到消息后,进行反序列化
- 服务消费者得到最终结果
RPC核心功能实现
实现服务寻址
服务寻址可以使用Call ID映射,在RPC中,所有的函数都必须有自己的一个ID,然后再客户端和服务端分别维护一个函数和Call ID的对应表。
当客户端需要进行远程调用时,通过查这个表,找出相应的Call ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应的代码。
实现方式:服务注册中心
要调用服务,首先需要一个服务注册中心去查询对方服务都有哪些实例,Dubbo的服务注册中心是可以配置的,官方推荐Zookeeper。
实现案例:RMI(Remote Method Invocation,远程方法调用):能够让在客户端Java虚拟机上的对象像调用本地对象一样调用服务端java 虚拟机中的对象上的方法。
Registry(服务发现):借助JNDI发布并调用了RMI服务。实际上JNDI就是一个注册表,服务端将服务对象放入注册表中,客户端从注册表中获取服务对象。
RMI服务在服务端实现之后需要注册到RMI Server上,然后客户端从指定的RMI地址上Lookup服务,调用该服务对应的方法即可完成远程方法调用。
Registry是个很重要的功能,当服务端开发完服务之后,要对外暴露,如果没有服务注册,则客户端是无从调用的,即使服务端的服务就在那里。
序列化和反序列化
因为只有二进制数据才能在网络中传输,所以需要序列化和反序列化。
网络传输
所有的数据都需要通过网络传输,因此就需要有一个网络传输层,网络传输层需要把Call ID和序列化后的参数字节流传给服务器,然后再把序列化后的调用结果传回给客户端。
只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。
尽管大部分 RPC 框架都使用 TCP 协议,但其实 UDP 也可以,而 gRPC 干脆就用了 HTTP2。
TCP 的连接是最常见的,简要分析基于 TCP 的连接:通常 TCP 连接可以是按需连接(需要调用的时候就先建立连接,调用结束后就立马断掉),也可以是长连接(客户端和服务器建立起连接之后保持长期持有,不管此时有无数据包的发送,可以配合心跳检测机制定期检测建立的连接是否存活有效),多个远程过程调用共享同一个连接。