浅谈RPC原理

一、概念定义

百度百科:

RPC(Remote Procedure Call ),即远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

个人理解:

通过某种指定的协议,可通过网络在指定的计算机上请求到需要的数据,在开发人员看来,即在需要获取到数据的一方(服务消费者Consumer),通过该协议,在接口提供方(服务提供者Provider)获取到数据的协议,被Sun公司进行定义,也是由Sun公司首先提出,这个协议被国际上普遍采用,这个称为RPC协议。

RPC框架的主要目标就是让远程服务调用更简单、透明。RPC框架负责屏蔽底层的传输方式(TCP或者UDP)、序列化方式(XML/JSON/二进制)和通信细节。开发人员在使用的时候只需要了解谁在什么位置提供了什么样的远程服务接口即可,并不需要关心底层通信细节和调用过程,允许像调用本地服务一样调用远程服务。

二、工作原理

调用流程

运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:
1.调用客户端句柄;执行传送参数
2.调用本地系统内核发送网络消息
3.消息传送到远程主机
4.服务器句柄得到消息并取得参数
5.执行远程过程

6.执行的过程将结果返回服务器句柄
7.服务器句柄返回结果,调用远程系统内核
8.消息传回本地主机
9.客户句柄由内核接收消息
10.客户接收句柄返回的数据

图解原理

三、直连模式下RPC简单实现

1.编服务提供者接口ProviderDemo

1
2
3
4
5
6
7
8
9
10
11
package com.yang.rpc.provider;
/**
* 服务提供者接口,用于暴露给服务消费者进行消费
*
*/
public interface ProviderDemo {
/**
* 服务提供者打印Msg方法
*/
public String printMsg(String msg);
}

2.服务提供者接口实现ProviderDemoImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.yang.rpc.provider;

/**
* 服务提供者实现类
*
*/
public class ProviderDemoImpl implements ProviderDemo {

public String printMsg(String msg) {
System.out.println("----传入的参数为" + msg + "----");
return "欢迎你 " + msg;
}
}

3.编写服务提供者ProviderServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.yang.rpc.provider;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
* 生产者运行的服务器
*
*/
public class ProviderServer {

public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {

//用于存放生产者服务接口的Map,实际的框架中会有专门保存服务提供者的
Map<String, Object> serviceMap = new HashMap();
serviceMap.put(ProviderDemo.class.getName(), new ProviderDemoImpl());

//服务器
ServerSocket server = new ServerSocket(8899);
System.out.println("服务等待调用");
while (true) {
Socket socket = server.accept();
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
String interfaceName = input.readUTF(); //获取服务消费者需要消费服务的接口名
String methodName = input.readUTF(); ////获取服务消费者需要消费服务的方法名

//参数的类型
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
//参数的对象
Object[] rpcArgs = (Object[]) input.readObject();

//执行调用过程
Class providerInteface = Class.forName(interfaceName); //得到接口Class
Object provider = serviceMap.get(interfaceName);//取得服务实现的对象

//获取需要执行的方法
Method method = providerInteface.getMethod(methodName, parameterTypes);
//通过反射进行调用
Object result = method.invoke(provider, rpcArgs);

//返回给客户端即服务消费者数据
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(result);
System.out.println("服务调用结束");
}
}
}

4.服务消费者ConsumerDemo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.yang.rpc.consumer;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;

import com.yang.rpc.provider.ProviderDemo;

/**
* 服务消费者
*
*/
public class ConsumerDemo {

public static void main(String[] args) throws NoSuchMethodException, IOException, ClassNotFoundException {
//获取服务提供者的接口名,一般RPC框架都是暴露服务提供者的接口定义
String providerInterface = ProviderDemo.class.getName();

//需要远程执行的方法,其实就是消费者调用生产者的方法
Method method = ProviderDemo.class.getMethod("printMsg", java.lang.String.class);

//需要传递的参数
Object[] rpcArgs = {"RPC!"};

Socket consumer = new Socket("127.0.0.1", 8899);

//将方法名称和参数 传递给服务生产者
ObjectOutputStream output = new ObjectOutputStream(consumer.getOutputStream());
output.writeUTF(providerInterface);
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(rpcArgs);

//从生产者读取返回的结果

ObjectInputStream input = new ObjectInputStream(consumer.getInputStream());
Object result = input.readObject();

System.out.println(result.toString());

}
}

5.开启服务

6.开启消费端进行调用后,服务端显示

7.开启消费端后,消费端返回结果

四、下载地址

点击我下载,小心你的鼠标

下个版本给大家讲解RPC框架Dubbo以及SpringBoot集成Dubbo