RPC基本概念
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务
本地过程调用:如果需要将本地student对象的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定 。
远程过程调用:上述操作的过程中,如果addAge()这个方法在服务端,执行函数的函数体在远程机器上,如何告诉机器需要调用这个方法呢?
- 首先客户端需要告诉服务器,需要调用的函数,这里函数和进程ID存在一个映射,客户端远程调用时,需要查一下函数,找到对应的ID,然后执行函数的代码 。
- 客户端需要把本地参数传给远程函数,本地调用的过程中,直接压栈即可,但是在远程调用过程中不再同一个内存里,无法直接传递函数的参数,因此需要客户端把参数转换成字节流,传给服务端,然后服务端将字节流转换成自身能读取的格式,是一个序列化和反序列化的过程 。
- 备好了之后,如何进行传输?网络传输层需要把调用的ID和序列化后的参数传给服务端,然后把计算好的结果序列化传给客户端,因此TCP层即可完成上述过程,gRPC中采用的是HTTP2协议 。
// Client端
// Student student = Call(ServerAddr, addAge, student)
1. 将这个调用映射为Call ID 。
2. 将Call ID,student(params)序列化,以二进制形式打包
3. 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
4. 等待服务器返回结果
5. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新
// Server端
1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap
2. 等待服务端请求
3. 得到一个请求后,将其数据包反序列化,得到Call ID
4. 通过在callIdMap中查找,得到相应的函数指针
5. 将student(params)反序列化后,在本地调用addAge()函数,得到结果
6. 将student结果序列化后通过网络返回给Client
![RPC远程调用原理浅析](http://img.jiangsulong.com/220410/002KA060-0.jpg)
文章插图
- 在微服务的设计中,一个服务A如果访问另一个Module下的服务B,可以采用HTTP REST传输数据,并在两个服务之间进行序列化和反序列化操作,服务B把执行结果返回过来 。
- 由于HTTP在应用层中完成,整个通信的代价较高,远程过程调用中直接基于TCP进行远程调用,数据传输在传输层TCP层完成,更适合对效率要求比较高的场景,RPC主要依赖于客户端和服务端之间建立Socket链接进行,底层实现比REST更复杂 。
消费者
API
让服务者和消费者都依赖API
![RPC远程调用原理浅析](http://img.jiangsulong.com/220410/002KC159-1.jpg)
文章插图
![RPC远程调用原理浅析](http://img.jiangsulong.com/220410/002K62620-2.jpg)
文章插图
在消费者创建ConsumerApp类使用代理对象
具体代码在ProxyUtils中
public class ConsumerApp {
public static void main(String[] args) {
//while死循环是为了测试调用提供者是否为随机
while (true) {
try {
Thread.sleep(2000);
// 获得代理对象
AddService addService = ProxyUtils.getProxy(AddService.class);
// 只要调用方法就会进入代理对象invoke方法
int result = addService.add(15, 684);
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
API中创建Request,AddService,ProxyUtils,ZkUtils
创建Request(该类为传输对象,必须实现序列化)
public class Request implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String interfaceName;
private String methodName;
private Object[] args;
public String getInterfaceName() {
return interfaceName;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
@Override
public String toString() {
return "Request [interfaceName=" + interfaceName + ", methodName=" + methodName + ", args="
推荐阅读
- 农村远程教育服务茶产业 为百万茶农致富导航
- 安溪,远程教育4+2学用模式助推茶业发展
- java多线程,静态方法加锁后,调用该方法会影响其它方法吗?
- CVE-2019-0193 Apache Solr远程代码执行漏洞 复现操作
- Windows 远程控制 Ubuntu 系统
- Linux Kernel 5.5 最终删除 SYSCTL 系统调用
- 空调清洗主要清洗哪里,空调清洗有必要吗
- 三步排查彻底解决远程桌面连接内部错误问题
- 苹果电脑MAC系统登录远程桌面 如何能够实现?
- 远程访问电脑设置教程