RPC框架技术初窥
RPC是什么
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
以上是百度百科对RPC的解释。
一个通俗的描述是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。
RPC产生的背景
早期单机时代,一台电脑上运行多个进程,大家各干各的,老死不相往来。假如A进程需要一个画图的功能,B进程也需要一个画图的功能,程序员就必须为两个进程都写一个画图的功能。这不是整人么?于是就出现了IPC(Inter-process communication,单机中运行的进程之间的相互通信)。OK,现在A既然有了画图的功能,B就调用A进程上的画图功能好了。
到了网络时代,大家的电脑都连起来了。以前程序只能调用自己电脑上的进程,能不能调用其他机器上的进程呢?于是就程序员就把IPC扩展到网络上,这就有了RPC。
这个时候画图功能就可以作为一个独立的服务提供给客户机使用。
RPC框架特性
RPC是协议
既然是协议就只是一套规范,那么就需要有人遵循这套规范来进行实现。目前典型的RPC实现包括:Dubbo、Thrift、GRPC、Hetty等。
网络协议和网络IO透明
既然RPC的客户端认为自己是在调用本地对象。那么传输层使用的是TCP/UDP还是HTTP协议,者是一些其他的网络协议它就不需要关心了。既然网络协议对其透明,那么调用过程中,使用的是哪一种网络IO模型调用者也不需要关心。
信息格式对其透明
在本地应用程序中,对象调用需要传递一些参数,会返回一个调用结果。对象内部是如何使用这些参数,并计算出处理结果的,调用方是不需要关心的。那么对于RPC来说,这些参数会以某种信息格式传递给网络上的另外一台计算机,这个信息格式是怎样构成的,调用方是不需要关心的。
有跨语言能力
调用方实际上也不清楚远程服务器的应用程序是使用什么语言运行的。那么对于调用方来说,无论服务器方使用的是什么语言,本次调用都应该成功,并且返回值也应该按照调用方程序语言所能理解的形式进行描述。
RPC框架的工作原理
1. 调用客户端句柄;执行传送参数
2. 调用本地系统内核发送网络消息
3. 消息传送到远程主机
4. 服务器句柄得到消息并取得参数
5. 执行远程过程
6. 执行的过程将结果返回服务器句柄
7. 服务器句柄返回结果,调用远程系统内核
8. 消息传回本地主机
9. 客户句柄由内核接收消息
10. 客户接收句柄返回的数据
自己实现RPC框架要做的工作
代码实现要做的工作
1、 设计对外的接口
1 2 3 4 5 | public interface IService extends Remote { public String queryName(String no) throws RemoteException; } |
2、 服务端的服务实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class ServiceImpl extends UnicastRemoteObject implements IService { private static final long serialVersionUID = 682805210518738166L; protected ServiceImpl() throws RemoteException { super(); } @Override public String queryName(String no) throws RemoteException { // 方法的具体实现 return String.valueOf(System.currentTimeMillis()); } } |
3、 RMI服务端实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class Server { public static void main(String[] args) { Registry registry = null; try { // 创建一个服务注册管理器 registry = LocateRegistry.createRegistry(8088); } catch (RemoteException e) { } try { // 创建一个服务 ServiceImpl server = new ServiceImpl(); // 将服务绑定命名 registry.rebind("vince", server); } catch (RemoteException e) { } } } |
4、 客户端实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class Client { public static void main(String[] args) { Registry registry = null; try { // 获取服务注册管理器 registry = LocateRegistry.getRegistry("127.0.0.1",8088); } catch (RemoteException e) { } try { // 根据命名获取服务 IService server = (IService) registry.lookup("vince"); // 调用远程方法 ,获取结果。 String result = server.queryName("ha ha ha ha"); } catch (Exception e) { } } } |