一、什么是RPC?
RPC(Remote Procedure Call)远程过程调用,是一种进程间的通信方式,其可以做到像调用本地方法那样调用位于远程的计算机的服务。其实现的原理过程如下:
- 本地的进程通过接口进行本地方法调用。
- RPC客户端将调用的接口名、接口方法、方法参数等信息利用网络通信发送给RPC服务器。
- RPC服务器对请求进行解析,根据接口名、接口方法、方法参数等信息找到对应的方法实现,并进行本地方法调用,然后将方法调用结果响应给RPC客户端。
二、实现RPC需要解决那些问题?
1. 约定通信协议格式
RPC分为客户端与服务端,就像HTTP一样,我们需要定义交互的协议格式。主要包括三个方面:
- 请求格式
- 响应格式
- 网络通信时数据的序列化方式
RPC请求
@Data public class RpcRequest { /** * 请求ID 用来标识本次请求以匹配RPC服务器的响应 */ private String requestId; /** * 调用的类(接口)权限定名称 */ private String className; /** * 调用的方法名 */ private String methodName; /** * 方法参类型列表 */ private Class<?>[] parameterTypes; /** * 方法参数 */ private Object[] parameters; }
RPC响应
@Data public class RpcResponse { /** * 响应对应的请求ID */ private String requestId; /** * 调用是否成功的标识 */ private boolean success = true; /** * 调用错误信息 */ private String errorMessage; /** * 调用结果 */ private Object result; }
2. 序列化方式
序列化方式可以使用JDK自带的序列化方式或者一些第三方的序列化方式,JDK自带的由于性能较差所以不推荐。我们这里选择JSON作为序列化协议,即将请求和响应对象序列化为JSON字符串后发送到对端,对端接收到后反序列为相应的对象,这里采用阿里的 fastjson 作为JSON序列化框架。
3. TCP粘包、拆包
TCP是个“流”协议,所谓流,就是没有界限的一串数据。大家可以想想河里
本文来源gao!%daima.com搞$代*!码9网(
的流水,是连成一片的,其间并没有分界线。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。粘包和拆包需要应用层程序来解决。
我们采用在请求和响应的头部保存消息体的长度的方式解决粘包和拆包问题。请求和响应的格式如下:
+--------+----------------+ | Length | Content | | 4字节 | Length个字节 | +--------+----------------+
4. 网络通信框架的选择
出于性能的考虑,RPC一般选择异步非阻塞的网络通信方式,JDK自带的NIO网络编程操作繁杂,Netty是一款基于NIO开发的网络通信框架,其对java NIO进行封装对外提供友好的API,并且内置了很多开箱即用的组件,如各种编码解码器。所以我们采用Netty作为RPC服务的网络通信框架。
三、RPC服务端
RPC分为客户端和服务端,它们有一个共同的服务接口API,我们首先定义一个接口 HelloService
public interface HelloService { String sayHello(String name); }