通过手写简易版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.mzph.cn/news/666251.shtml

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

相关文章

【实证分析】地级市-资本存量测算结果数据集(含计算公式及原始数据)( 2003-2021年)

该数据为地级市资本存量测算&#xff08;2003-2021年&#xff09;&#xff0c;提供了中国地级市在该期间内资本存量的详细测算结果&#xff0c;包括两种基于2011年和2006年基期的测算方式。该数据集利用了从城市统计年鉴和中国统计年鉴获取的固定资产投资数据及其增速&#xff…

P8818 [CSP-S 2022] 策略游戏

[CSP-S 2022] 策略游戏 题目描述 小 L 和小 Q 在玩一个策略游戏。 有一个长度为 n n n 的数组 A A A 和一个长度为 m m m 的数组 B B B&#xff0c;在此基础上定义一个大小为 n m n \times m nm 的矩阵 C C C&#xff0c;满足 C i j A i B j C_{i j} A_i \times …

微软的Copilot for Sales(销售助手)和Copilot for Service(服务助手)现已全面开放

深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领域的领跑者。点击订阅&#xff0c;与未来同行&#xff01; 订阅&#xff1a;https://rengongzhineng.io/ 。 微…

C++ 动态规划 线性DP 数字三角形

给定一个如下图所示的数字三角形&#xff0c;从顶部出发&#xff0c;在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点&#xff0c;一直走到底层&#xff0c;要求找出一条路径&#xff0c;使路径上的数字的和最大。 73 8 8 1 02 7 4 4 4 5 2 6 5 输入格式 …

CMake生成osg的FFMPEG插件及Windows下不生成VS工程问题解决

在Windows下&#xff0c;如何利用CMake生成osg的FFMPEG插件&#xff0c;请参考如下博文&#xff0c;同生成jpeg插件类似&#xff1a; osg第三方插件的编译方法&#xff08;以jpeg插件来讲解&#xff09;。 如下为生成FFMPEG时必要的设置&#xff1a; 注意&#xff1a; 一定要…

【精选】java继承、object、封装初阶(子类,父类继承)

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

JS document对象详解

JS document对象详解 1.document对象简介2.document对象常用的属性document.URLdocument.referrer 3.document对象方法document.writedocument.writeln 1.document对象简介 document对象其实是window对象下的一个子对象&#xff0c;它操作的是HTML文档里所有的内容。实际上&am…

[文本挖掘和知识发现] 03.基于大连理工情感词典的情感分析和情绪计算

作者于2023年8月新开专栏——《文本挖掘和知识发现》&#xff0c;主要结合Python、大数据分析和人工智能分享文本挖掘、知识图谱、知识发现、图书情报等内容。这些内容也是作者《文本挖掘和知识发现&#xff08;Python版&#xff09;》书籍的部分介绍&#xff0c;本书预计2024年…

获取未来的5分钟整点时间05,10,15,20,25...

比如预约网约车的时候&#xff0c;是按5分钟的整点时间 GetMapping("/getFiveNextTime")public String fiveNextTime(RequestParam(defaultValue "0") Integer interval) {Calendar calendar Calendar.getInstance();calendar.add(Calendar.MINUTE, (5 …

二、人工智能之提示工程(Prompt Engineering)

黑8说 岁月如流水匆匆过&#xff0c;哭一哭笑一笑不用说。 黑8自那次和主任谈话后&#xff0c;对这个“妖怪”继续研究&#xff0c;开始学习OpenAI API&#xff01;关注到了提示工程(Prompt Engineering)的重要性&#xff0c;它包括明确的角色定义、自然语言理解&#xff08;…

软件测试学习笔记-测试用例的编写

7中测试分类 按照阶段可划分单元测试、集成测试、系统测试、验收测试。代码可见度划分黑盒测试、灰盒测试、白盒测试 单元测试&#xff1a;针对源代码的测试 集成测试&#xff1a;针对接口进行测试 系统测试&#xff1a;针对功能和非功能的测试 验收测试&#xff1a;公测、内测…

STM32外部中断(红外传感器与旋转编码器计数案例)

文章目录 一、介绍部分简介中断系统中断执行流程STM32中断NVIC基本结构NVIC优先级分组外部中断外部中断简介外部中断基本结构外部中断的流程AFIOEXTI框图 相关外设介绍旋转编码器介绍硬件电路对射式红外传感器 二、代码实现对射式红外传感器计次连接电路封装红外传感器与中断函…

【开源】基于JAVA+Vue+SpringBoot的河南软件客服系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理人员2.2 业务操作人员 三、系统展示四、核心代码4.1 查询客户4.2 新增客户跟进情况4.3 查询客户历史4.4 新增服务派单4.5 新增客户服务费 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的河…

2024/2/3学习记录

微信小程序 小程序中组件的分类 视图容器 view 普通视图区域&#xff0c;类似于 div 常用来实现页面的布局效果。 scroll-view 可滚动的视图区域&#xff0c;常用来实现滚动列表效果 swiper 和 swiper-item 常用 swiper 组件的常用属性 轮播图容器组件和轮播图item组件 基…

记录 arm 开发板上 nginx 配置 http 服务注意事项

1. 自定义项目&#xff0c;需要在 conf.d 目录中增加一个 .conf 配置文件&#xff1a; server {listen 9200; # 端口号server_name localhost; # 服务名称location / {root /home/imx6q/media; # 项目根目录&#xff08;需要修改 n…

Vue引入Axios

1.命令安装axios和vue-axios npm install axios --save npm install vue-axios --save 2.package.json查看版本 3.在main.js中引用 import axios from axios; import VueAxios from vue-axios; Vue.use(VueAxios,axios) 4.如何使用 &#xff08;初始化方法&#xff09; 将下列代…

HCIA-Datacom实验指导手册:4.2 实验二:AAA配置实验

HCIA-Datacom实验指导手册&#xff1a;3.3 实验三&#xff1a;以太网链路聚合实验 一、实验介绍&#xff1a;display ssh server ip-block all通过Telnet登录时&#xff0c;解除对IP地址10.1.2.3的锁定。通过STelnet登录时&#xff0c;解除对IP地址10.1.2.3的锁定。解除对用户名…

有什么不同? Elastic 数据层和 Amazon OpenSearch Service 层

作者&#xff1a;来自 Elastic Ugo Sangiorgi 了解 Elastic 和 Amazon OpenSearch Service 数据层之间的主要差异&#xff0c;以实现更智能、经济高效的数据管理。 在数据管理领域&#xff0c;在讨论如何在不同的性能要求下提供和/或保留数据时&#xff0c;经常会出现 “热 (h…

04、全文检索 -- Solr -- 管理 Solr 的 core(使用命令和图形界面创建、删除 core,以及对core 目录下的各文件进行详细介绍)

目录 管理 Solr 的 core创建 Core方式1&#xff1a;solr 命令创建演示&#xff1a;使用 solr 命令创建 Core&#xff1a;演示&#xff1a;命令删除 Core&#xff08;彻底删除&#xff09; 方式2&#xff1a;图形界面创建Web控制台创建CoreWeb控制台删除 Core&#xff08;未彻底…

C语言:详解操作符(上)

摘要&#xff1a; 本篇&#xff0c;我们将学习C语言中操作符的相关内容&#xff0c;操作符是C语言中重要的元素之一&#xff0c;在我们的代码中处处都有&#xff0c;下面我们将详细介绍操作符的相关内容&#xff0c;并结合一些代码例题加深印象。 目录 一、操作符的分类及介绍 …