通过手写简易版RPC理解RPC原理

RPC是什么


所谓的RPC其实是为了不同主机的两个进程间通信而产生的,通常不同的主机之间的进程通信,程序编写需要考虑到网络通信的功能,这样程序的编写将会变得复杂。RPC就来解决这一问题的,一台主机上的进程对另外一台主机的进程发起请求时,内核会将请求转交给RPC client,RPC client经过报文的封装转交给目标主机的RPC server,RPC server就将报文进行解析,还原成正常的请求,转交给目标主机上的目标进程。在我们看来在就像是在同一台主机上的两个进程通信一样,完全没有意识到是在不同的主机上。因此RPC其实也可以看做是一种协议或者是编程框架,目的是为了简化分布式程序的编写。

RPC基本流程

大致过程如下:

Rpc Client通过传入的IP、端口号、调用类以及方法的参数,通过动态代理找到具体的调用类的方法,将请求的类、方法序列化,传输到服务端;

当Rpc Service收到请求以后,将传入类和方法反序列化,通过反射找到对应的类的方法进行调用,最后将返回结果进行序列化,返回给客户端;

Rpc Client收到返回值以后,进行反序列化,最后将结果展示;

DEMO

假如我现在有一个HelloService服务,有一个say的方法,如下:

package com.cjian.rpc.provider;/*** @Author: cjian* @Date: 2023/6/21 10:24* @Des:*/
public interface HelloService {String say(String name);
}

实现类如下:

package com.cjian.rpc.provider;/*** @Author: cjian* @Date: 2023/6/21 10:25* @Des:*/
public class HelloServiceImpl implements HelloService {@Overridepublic String say(String name) {return "您好, " + name;}
}

现在另一个服务需要调用say方法,如何使用rpc思想实现呢?

模拟注册中心:

package com.cjian.rpc.registercenter;import java.io.IOException;/*** @Author: cjian* @Date: 2023/6/21 10:25* @Des:*/
public interface Server {void stop();void start() throws IOException;void register(Class serviceInterface, Class impl);boolean isRunning();int getPort();
}
package com.cjian.rpc.registercenter;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @Author: cjian* @Date: 2023/6/21 10:26* @Des:*/
public class ServiceCenter implements Server {private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());private static final HashMap<String, Class> serviceRegistryCenter = new HashMap<String, Class>();private static boolean isRunning = false;private static int port;public ServiceCenter(int port) {this.port = port;}public void stop() {isRunning = false;executor.shutdown();}public void start() throws IOException {ServerSocket server = new ServerSocket();server.bind(new InetSocketAddress(port));System.out.println("start server");try {while (true) {// 1.监听客户端的TCP连接,接到TCP连接后将其封装成task,由线程池执行executor.execute(new ServiceTask(server.accept()));}} finally {server.close();}}public void register(Class serviceInterface, Class impl) {serviceRegistryCenter.put(serviceInterface.getName(), impl);}public boolean isRunning() {return isRunning;}public int getPort() {return port;}private static class ServiceTask implements Runnable {Socket client = null;public ServiceTask(Socket client) {this.client = client;}public void run() {ObjectInputStream input = null;ObjectOutputStream output = null;try {// 2.将客户端发送的码流反序列化成对象,反射调用服务实现者,获取执行结果input = new ObjectInputStream(client.getInputStream());String serviceName = input.readUTF();String methodName = input.readUTF();Class<?>[] parameterTypes = (Class<?>[]) input.readObject();Object[] arguments = (Object[]) input.readObject();Class serviceClass = serviceRegistryCenter.get(serviceName);if (serviceClass == null) {throw new ClassNotFoundException(serviceName + " not found");}Method method = serviceClass.getMethod(methodName, parameterTypes);Object result = method.invoke(serviceClass.newInstance(), arguments);// 3.将执行结果反序列化,通过socket发送给客户端output = new ObjectOutputStream(client.getOutputStream());output.writeObject(result);} catch (Exception e) {e.printStackTrace();} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}if (client != null) {try {client.close();} catch (IOException e) {e.printStackTrace();}}}}}
}

客户端:

package com.cjian.rpc.client;import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;/*** @Author: cjian* @Date: 2023/6/21 10:32* @Des:*/
public class RPCClient<T> {public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress addr) {// 1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface},new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Socket socket = null;ObjectOutputStream output = null;ObjectInputStream input = null;try {// 2.创建Socket客户端,根据指定地址连接远程服务提供者socket = new Socket();socket.connect(addr);// 3.将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者output = new ObjectOutputStream(socket.getOutputStream());output.writeUTF(serviceInterface.getName());output.writeUTF(method.getName());output.writeObject(method.getParameterTypes());output.writeObject(args);// 4.同步阻塞等待服务器返回应答,获取应答后返回input = new ObjectInputStream(socket.getInputStream());return input.readObject();} finally {if (socket != null) socket.close();if (output != null) output.close();if (input != null) input.close();}}});}
}

测试:

package com.cjian.rpc;import com.cjian.rpc.client.RPCClient;
import com.cjian.rpc.provider.HelloService;
import com.cjian.rpc.provider.HelloServiceImpl;
import com.cjian.rpc.registercenter.Server;
import com.cjian.rpc.registercenter.ServiceCenter;import java.io.IOException;
import java.net.InetSocketAddress;/*** @Author: cjian* @Date: 2023/6/21 10:33* @Des:*/
public class Test {public static void main(String[] args) {// 模拟注册中心new Thread(() -> {try {Server serviceServer = new ServiceCenter(8088);// 将服务注册到注册中心serviceServer.register(HelloService.class, HelloServiceImpl.class);// 开启注册中心serviceServer.start();} catch (IOException e) {e.printStackTrace();}}).start();// 客户端远程调用HelloService service = RPCClient.getRemoteProxyObj(HelloService.class, new InetSocketAddress("localhost", 8088));System.out.println(service.say("test"));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/450974.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jupyter notebook更改工作目录的2个细节

详细步骤参考知乎原文&#xff1a; 如何更改Jupyter Notebook的默认工作路径&#xff1f; - 知乎 (zhihu.com​​​​​​) 步骤4中需要删除 #符号和后面的空格&#xff01;一定要删除空格&#xff0c;否则会出现语法错误的报错 步骤5中&#xff0c;经过评论区提醒后&#xf…

三维重建方法3D gaussian splatting与NeRF的区别和异同

最近学习了一些三维重建相关的内容&#xff0c;目前比较主要的重建流派就是3DGS以及NeRF&#xff0c;NeRF作为2020年发布的文章轰动一时&#xff0c;影响深远&#xff0c;有很多NeRF based的相关工作在这些年涌现。3DGS作为2023年的new talk of the town&#xff0c;其在保证合…

HBase 数据导入导出

HBase 数据导入导出 1. 使用 Docker 部署 HBase2. HBase 命令查找3. 命令行操作 HBase3.1 HBase shell 命令3.2 查看命名空间3.3 查看命名空间下的表3.4 新建命名空间3.5 查看具体表结构3.6 创建表 4. HBase 数据导出、导入4.1 导出 HBase 中的某个表数据4.2 导入 HBase 中的某…

深度学习手写字符识别:训练模型

说明 本篇博客主要是跟着B站中国计量大学杨老师的视频实战深度学习手写字符识别。 第一个深度学习实例手写字符识别 深度学习环境配置 可以参考下篇博客&#xff0c;网上也有很多教程&#xff0c;很容易搭建好深度学习的环境。 Windows11搭建GPU版本PyTorch环境详细过程 数…

代码随想录算法训练营DAY11 | 栈与队列 (2)

一、LeetCode 20 有效的括号 题目链接&#xff1a;20.有效的括号https://leetcode.cn/problems/valid-parentheses/ 思路&#xff1a;遇到左括号直接进栈&#xff1b;遇到右括号判断站顶是否有匹配的括号&#xff0c;没有就返回flase&#xff0c;有就将栈顶元素出栈&#xff1…

ctfshow——文件包含

文章目录 web 78——php伪协议第一种方法——php://input第二种方法——data://text/plain第三种方法——远程包含&#xff08;http://协议&#xff09; web 78——str_replace过滤字符php第一种方法——远程包含&#xff08;http://协议&#xff09;第二种方法——data://&…

Nicn的刷题日常之杨氏矩阵(三种方法求解,逐级递增详解,手把手教学,建议三连收藏)

目录 1.杨氏矩阵知识普及&#xff1a;什么是样式矩阵 2.题目描述 3.解题 3.1暴力求解&#xff0c;遍历法 3.2巧妙解题&#xff1a;对角元素法 3.3将巧解法封装为函数 4.结语 1.杨氏矩阵知识普及&#xff1a;什么是样式矩阵 杨氏矩阵&#xff0c;是对组合表示理论和…

蓝牙 - BLE Basics

BLE Basics [ BLE基础 ] 需要了解的两个主要概念是 BLE 设备的两种模式&#xff1a; Two major concepts to know about are the two modes of BLE devices: * 广播模式&#xff08;也称为通用访问配置文件 GAP&#xff09; * 连接设备模式&#xff08;也称为通用属性配置文件…

halcon中的坐标系相关

一、定义 世界坐标系&#xff1a;真实世界中物体实际位置&#xff08;三维&#xff09; 相机坐标系&#xff1a;以镜头光心为原点&#xff0c;光轴为Z轴&#xff08;三维&#xff09; 图像物理坐标系&#xff1a;以成像图像中心维原点&#xff08;二维&#xff09; 像素坐标系…

如何计算两个指定日期相差几年几月几日

一、题目要求 假定给出两个日期&#xff0c;让你计算两个日期之间相差多少年&#xff0c;多少月&#xff0c;多少天&#xff0c;应该如何操作呢&#xff1f; 本文提供网页、ChatGPT法、VBA法和Python法等四种不同的解法。 二、解决办法 1. 网页计算法 这种方法是利用网站给…

数据中心机房建设的关键痛点及解决方案

随着信息技术的飞速发展&#xff0c;数据中心机房已成为企业信息系统的核心。然而&#xff0c;在机房系统的建设过程中&#xff0c;投资及运行维护成为项目管理的关键痛点。合理的投资决策和高效的运维管理是确保机房系统经济性和可靠性的重要因素。本文将探讨机房系统建设的投…

学习Spring的第十三天

Repository : 注解Dao层 Service : 注解Service层 Controller : 注解Web层 值得注意的是 : 当业务中出现一个bean三层都不属于时 , 我们用Component进行注解 Bean依赖注入注解开发 : Value : 可把zhangsan注解进username属性 Value("zhangsan")private String …