RPC是什么
Remote Procedure Call,远过程调用
所谓远过程调用通俗地讲,就是某个程序调用了远方另一个非本程序的方法,本质上就是进程之间的网络通信。更进一步说,每一个进程都有一个自己的地址空间,如果该进程调用的方法存在于本进程的地址空间,那么就是普通的本地调用;若该进程调用的方法不存在本进程的地址空间中,而是在另一个进程的地址空间中,那么就是远过程调用。
RPC 与 HTTP
不同程序之间的通信方式有很多,比如浏览器和服务器之间的 HTTP 协议。与 RPC 相比,HTTP 有统一的标准,因此更加通用、兼容性好,支持不同语言。而且基于文本的 HTTP 有很好的可读性。但是 HTTP 缺点也很明显:
- 基于 HTTP 的报文存在过多的冗余信息,而 RPC 常采用自定义的协议格式,内容更加直接,减少报文冗余;
- RPC 更加可定制化,可以采用更加高效的二进制序列化协议,将文本转换成二进制传输,获得更高的性能;
实现一个简单 RPC 框架
分析一下实现一个简单 rpc 框架的过程。
C/S模型
方法的请求者就是client方,方法的提供者就是我们的server方,两者通过网络通信来传递数据。
- client端。client方要使用服务,最终就是要使用方法,所以client至少需要提供:
- 调用方法的类,即谁来调用方法;
- 方法名,方法参数,即确定调用哪一个方法;
- server端。server端主要就是提供方法调用的服务,所以需要有以下的职责:
- 注册可用方法,要提供方法首先就要有方法;
- 获取服务,返回服务结果,即给client提供远程调用的服务;
public interface ServiceCenter {
// 开始监听网络端口,提供服务
void start() throws IOException;
// 停止服务
void stop();
// 注册服务接口及对应实现
void register(Class<?> serviceInterface, Class<?> impl);
// 用于客户获取服务端口
int getPort();
}
网络通信
远程的通信需要通过网络来实现,网络的传输本质上就是字节序列的搬运,所以无论是客户端发出的请求还是服务端返回的服务,都需要序列化成字节序列发送。对象变成字节序列使用的是序列化方法,反之从字节序列还原一个对象使用的就是反射的方式了。
为了能够很好的解释来自客户端的 rpc 请求到底是什么含义,双方都需要对请求的结构达成一个协议:
public class RpcRequest implements Serializable {
private String serviceName; // 服务对象的名称
private String methodName; // 要使用服务对象的哪个方法
private Class<?>[] paraTypes; // 方法参数类型
private Object[] params; // 方法参数列表
// constructor, getters and setters ...
}
server 服务提供
服务中心启动之后的主要任务就是:监听网络请求,获取到请求后,根据请求的参数从注册中心中找到方法并调用,接着通过网络返回结果。所以服务中心服务提供的主要逻辑如下:
public void start() throws IOException {
// 1. 服务中心拥有了socket才打开了网络的通信的门
try (ServerSocket serverSocket = new ServerSocket()) {
serverSocket.bind(new InetSocketAddress(port)); // socket = (ip地址,端口)
System.out.println("Service center start :)");
while (true) {
// 2. 阻塞等待客户连接
// 3. 接收到tcp请求就交给一个线程进行处理
threadPool.execute(new ServiceTask(serverSocket.accept()));
}
}
}
可以发现在服务端 socket 接收到一个连接后,再进行了一次包装成 ServiceTask。ServiceTask 是一个自定义的类,封装了 rpc 请求的处理,里边的主要逻辑是:
- 通过 socket 获取 tcp 的字节流输入,进而还原出一个 rpc 请求对象;
- 根据 rpc 请求的各个参数从注册中心获取服务对象;
- 服务对象调用方法得到结果,再通过 socket 写回给客户端。
client 服务获取
客户端最终是通过反射字节码的方式得到服务的,所以可以使用 JDK 提供的动态代理方法来动态获取一个服务对象的代理,接着通过该代理对象来间接调用远程服务器的方法。所以与服务器的网络通信实际上在代理对象内部进行完成,客户只是把这个代理对象当作其服务的提供者。