当前位置: 首页 > article >正文

手写简易RPC(实践版)

首先了解rpc

rpc-远程过程调用,openFeign,Dubbo都可以算作rpc,以微服务来具体说明,就是在本地不需要去发送请求,通过rpc框架,像调用本地方法一样调用其他服务的方法,本质上还是要经过网络,去请求其他服务的资源

一般rpc都会有一个注册中心,使用rpc框架的服务称为消费者,被调用的服务成为生产者,以下是简单的图示

这里的服务可以简单理解为类中的方法或者类

接下来实际去手写一个最简单rpc框架

首先准备一个多模块的项目,结构如下

cosumer为消费者,provider为生产者,rpcFrame是我们要写的核心,也就是rpc框架的内容

这个过程就像工厂车间,从生产出来,到工厂加工,然后到消费者手上,按照这个顺序去编写程序理解也会更轻松一些

先把整体文件目录结构放出来,找不到的可以对应一下

按照车间的生产顺序,首先要 provider 生产产品

public interface HelloService {public String sayHello(String name);
}
public class HelloServiceImpl implements HelloService{@Overridepublic String sayHello(String name) {System.out.println("Hello " + name);return "Hello " + name;}
}

这里的产品就是提供一个方法以及其实现类,一个简单的hello

产品有了,下一步就是加工,加工要有仓库,那么我们在rpcFrame中创建一个本地的“仓库”(注册中心)

public class LocalRegister {public  static final Map<String, Class<?>> map = new ConcurrentHashMap<String, Class<?>>();/*** 注册*/public static void register(String interfaceName, Class<?> implClass) {map.put(interfaceName, implClass);}/*** 获取服务*/public static Class<?> get(String interfaceName) {return map.get(interfaceName);}
}

因为可能不止一个产品,所以需要一个集中的有包容性的仓库,那么在Java中这个仓库就是Map

key为服务的名称,应当是provider服务,不过后续为了方便记录这里的key是类接口的名称,val则是其实现类

然后回到我们的生产车间 provider,生产好的产品需要送入仓库,在provider中添加主函数

public class Main {public static void main(String[] args) {//注册服务LocalRegister.register(HelloService.class.getName(), HelloServiceImpl.class);//启动网络服务HttpServer httpServer = new HttpServer();httpServer.startServer();}
}

注册服务就是我们将产品送入仓库的过程,但是在Java中主函数不能作为一个服务,所以我们使用需要一个网络框架,这里选用的是tomcat

把启动服务的工作一并交给仓库加工,所以这里调用的是rpcFrame的方法,如下

public class HttpServer {//主要是启动服务public void startServer(){Tomcat tomcat = new Tomcat();//服务器网络参数,可以通过参数传递,我们写的是简单版rpc,直接写死即可tomcat.setBaseDir("/");tomcat.setPort(8081);tomcat.setHostname("127.0.0.1");Context context = tomcat.addContext("/", null);// 添加上下文,映射路径为'/'// 初始化参数 (可选)context.addParameter("param1", "value1");context.addParameter("param2", "value2");context.addErrorPage(new ErrorPage());context.setCookies(true);context.setSessionTimeout(30);// 为context上下文添加servlet--处理请求// 这些是Javaweb的相关知识,如果不熟悉再去了解一下Tomcat.addServlet(context,"defaultServlet",new IndexServlet());// 为名称defaultServlet的servlet添加一个映射路径为'v1'context.addServletMappingDecoded("/*","defaultServlet");//为 servlet 配置 URL 映射try {tomcat.start();} catch (LifecycleException e) {throw new RuntimeException(e);}tomcat.getServer().await();//阻塞}
}

以上的大部分内容作者也不太会,因为springboot自带的tomcat,没用过原生的tomcat,网上随便找的启动方式稍微调整一下就可以用了

有服务端之后需要对请求进行处理,那么就涉及servlet的知识了(这里如果看不懂稍后结合消费者一端请求再理解一下)

调用服务,肯定要添加参数,那么这个参数也需要一个类来承载,准备一个RpcRequest类,其中的Serializable 一定要实现,因为在网络中无法传递Java对象,必须要实现序列化

@Data
@AllArgsConstructor
public class RpcRequest implements Serializable {//序列化很重要/*** 服务名称*/private String serviceName;/*** 方法名称*/private String methodName;/*** 参数类型列表*/private Class<?>[] parameterTypes;/*** 参数列表*/private Object[] args;}
public class IndexServlet extends HttpServlet {// 处理http请求@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置响应的内容类型和字符编码resp.setContentType("text/plain");resp.setCharacterEncoding("UTF-8");try  {ServletInputStream inputStream = req.getInputStream();ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);// 读取请求对象RpcRequest request = (RpcRequest) objectInputStream.readObject();// 获取服务类和方法Class<?> serviceClass = LocalRegister.get(request.getServiceName());Method method = serviceClass.getMethod(request.getMethodName(), request.getParameterTypes());// 创建服务实例Object serviceInstance = serviceClass.getDeclaredConstructor().newInstance();// 通过反射直接调用方法Object result = method.invoke(serviceInstance, request.getArgs());// 返回结果resp.getOutputStream().write(result.toString().getBytes());} catch (Exception e) {System.out.println("Error occurred while processing request");}}
}

下一步就可以先启动服务端,把“生产+送进车间”完成

服务启动成功

消费者就很简单,只需要构造一个参数,然后去向rpc消费就可以了

public class Main {public static void main(String[] args) throws Exception {//简单构造参数,这里还可以向上封装一层,使其更加简单易用,比较流行的rpc框架也是这么做的//不过我们主要是为了学习了解rpc,以完成其基本功能为主,择取最简单路径RpcRequest rpcRequest = new RpcRequest(HelloService.class.getName(),"sayHello",new Class[]{String.class},new Object[]{"张三"});//调用String s = HttpClient.sendHttpRequest("127.0.0.1", 8081, rpcRequest);System.out.println(s);}
}

rpc归根到底还是服务间的通讯,离不开网络,把这个网络请求放到rpc里,我们只需要调用方法即可,具体的请求在rpcFrame中如下

public class HttpClient {public static String sendHttpRequest(String host, Integer port, RpcRequest request) throws IOException {URL url = new URL("http", host, port, "/");HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("POST");connection.setDoOutput(true);connection.setRequestProperty("Content-Type", "application/json");try (OutputStream outputStream = connection.getOutputStream();ObjectOutputStream oot = new ObjectOutputStream(outputStream)) {oot.writeObject(request);oot.flush();}//获取结果try (InputStream inputStream = connection.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {StringBuilder result = new StringBuilder();String line;while ((line = reader.readLine()) != null) {result.append(line).append("\n");}return result.toString();}}
}

这里的内容与rpc关系不大,只需要知道,这里是发送http请求的即可(因为我也是网上搜+AI调整的)

以上就是一个最简单的rpc框架,我来梳理一下具体的流程

首先provider将自己的服务HelloService注册到rpcFrame的注册中心,也就是前面的map中。

然后provider需要启动一个网络服务,成为一个提供服务的服务器;此时的rpc中包含一个provider发起的网络,网络里有一个服务HelloService

cosumer构造好必要的参数,向rpc发起请求,rpc通过http请求,向存在的provider的网络请求资源,执行对应的方法,然后将结果进行处理返回给cosumer

可能会出问题的地方

① 由于使用的是原始的方式启动tomcat,可能会出现版本与具体的类出现冲突,导致服务无法启动,这是我的Tomcat版本

        <dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>8.5.97</version></dependency>

②如果没有多模块开发的经验单纯想了解rpc发现有些类爆红无法被发现,记得去导入对应的模块

③网络协议相关的内容,本人也不是很了解,就是遇到问题解决问题,如果说有网络相关的问题,建议百度,有大佬如果不用tomcat也可以用其他网络框架,netty等都可以

④操作请求的时候IO操作较多,注意资源的问题,解决不了直接问AI,这种资源泄露等相关问题对于AI都是小儿科

相关文章:

手写简易RPC(实践版)

首先了解rpc rpc-远程过程调用&#xff0c;openFeign&#xff0c;Dubbo都可以算作rpc&#xff0c;以微服务来具体说明&#xff0c;就是在本地不需要去发送请求&#xff0c;通过rpc框架&#xff0c;像调用本地方法一样调用其他服务的方法&#xff0c;本质上还是要经过网络&…...

mysql学习笔记-多版本并发控制

1、什么是ReadView 在 MVCC机制中&#xff0c;多个事务对同一个行记录进行更新会产生多个历史快照&#xff0c;这些历史快照保存在 Undo Log里。如果一个事务想要查询这个行记录&#xff0c;需要读取哪个版本的行记录呢?这时就需要用到 ReadView 了&#xff0c;它帮我们解决了…...

问题记录汇总

记录一些问题 如何分析错误帧问题-CSDN博客...

算法日记20:SC72最小生成树(prim朴素算法)

一、题目&#xff1a; 二、题解 2.1&#xff1a;朴素prim的步骤解析 O ( n 2 ) O(n^2) O(n2)(n<1e3) 0、假设&#xff0c;我们现在有这样一个有权图 1、我们随便找一个点&#xff0c;作为起点开始构建最小生成树(一般是1号)&#xff0c;并且存入intree[]状态数组中&#xf…...

requests.exceptions.JSONDecodeError: Expecting value: line 2 column 1 (char 1)

requests.exceptions.JSONDecodeError: Expecting value: line 2 column 1 (char requests.exceptions.JSONDecodeError 是 Python 中使用 requests 库进行 HTTP 请求时&#xff0c;当期望返回的响应体为 JSON 格式&#xff0c;但实际响应体不符合 JSON 格式时出现的错误。这个…...

Redis7——基础篇(五)

前言&#xff1a;此篇文章系本人学习过程中记录下来的笔记&#xff0c;里面难免会有不少欠缺的地方&#xff0c;诚心期待大家多多给予指教。 基础篇&#xff1a; Redis&#xff08;一&#xff09;Redis&#xff08;二&#xff09;Redis&#xff08;三&#xff09;Redis&#x…...

spring boot知识点1

1.什么是spring boot spring boot是spring框架的子项目&#xff0c;主要特点是自动配置&#xff0c;以及内置的tomcat服务器&#xff0c;适合快速开发web与微服务架构 2.spring boot和spring cloud俩者之间的联系 spring boot可单独运行&#xff0c; spring cloud则是用于多…...

从零搭建微服务项目Base(第7章——微服务网关模块基础实现)

前言&#xff1a; 在前面6章的学习中已经完成了服务间的调用实现&#xff0c;即各微服务通过nacos或eureka服务器完成服务的注册&#xff0c;并从nacos中拉取配置实现热更新。当某个服务接口需要调用其他服务时&#xff0c;通过feign定义接口&#xff0c;并通过注解配置服务名…...

pdf转换成word在线 简单好用 支持批量转换 效率高 100%还原

pdf转换成word在线 简单好用 支持批量转换 效率高 100%还原 在数字化办公的浪潮中&#xff0c;文档格式转换常常让人头疼不已&#xff0c;尤其是 PDF 转 Word 的需求极为常见。PDF 格式虽然方便阅读和传输&#xff0c;但难以编辑&#xff0c;而 Word 格式却能灵活地进行内容修…...

嵌入式音视频开发(二)ffmpeg音视频同步

系列文章目录 嵌入式音视频开发&#xff08;零&#xff09;移植ffmpeg及推流测试 嵌入式音视频开发&#xff08;一&#xff09;ffmpeg框架及内核解析 嵌入式音视频开发&#xff08;二&#xff09;ffmpeg音视频同步 嵌入式音视频开发&#xff08;三&#xff09;直播协议及编码器…...

SpringBoot速成概括

视频&#xff1a;黑马程序员SpringBoot3Vue3全套视频教程&#xff0c;springbootvue企业级全栈开发从基础、实战到面试一套通关_哔哩哔哩_bilibili 图示&#xff1a;...

微信小程序image组件mode属性详解

今天学习微信小程序开发的image组件&#xff0c;mode属性的属性值不少&#xff0c;一开始有点整不明白。后来从网上下载了一张图片&#xff0c;把每个属性都试验了一番&#xff0c;总算明白了。现总结归纳如下&#xff1a; 1.使用scaleToFill。这是mode的默认值&#xff0c;sc…...

Matlab写入点云数据到Rosbag

最近有需要读取一个点云并做处理后&#xff0c;重新写回rosbag。网上有很多读取的教程&#xff0c;但没有写入。自己写入时也遇到了很多麻烦&#xff0c;踩了一堆坑进行记录。 1. rosbag中一个lidar的msg有哪些信息&#xff1f; 通过如下代码&#xff0c;先读取一个rosbag的l…...

数据分析--数据清洗

一、数据清洗的重要性&#xff1a;数据质量决定分析成败 1.1 真实案例警示 电商平台事故&#xff1a;2019年某电商大促期间&#xff0c;因价格数据未清洗导致错误标价&#xff0c;产生3000万元损失医疗数据分析&#xff1a;未清洗的异常血压值&#xff08;如300mmHg&#xff…...

iNeuOS工业互联网操作系统,民爆远程运维平台案例

iNeuOS工业互联网操作系统,民爆远程运维平台案例 目 录 1. 概述... 2 2. iNeuOS在民爆生产厂区和北京运维中心配置... 3 1.1 生产厂区配置... 3 1.2 运维中心配置... 7 1. 概述 针对本项目进行初步调研,项目的总体需求为满足新建…...

用命令模式设计一个JSBridge用于JavaScript与Android交互通信

用命令模式设计一个JSBridge用于JavaScript与Android交互通信 在开发APP的过程中&#xff0c;通常会遇到Android需要与H5页面互相传递数据的情况&#xff0c;而Android与H5交互的容器就是WebView。 因此要想设计一个高可用的 J S B r i d g e JSBridge JSBridge&#xff0c;不…...

Vue 3最新组件解析与实践指南:提升开发效率的利器

目录 引言 一、Vue 3核心组件特性解析 1. Composition API与组件逻辑复用 2. 内置组件与生命周期优化 3. 新一代UI组件库推荐 二、高级组件开发技巧 1. 插件化架构设计 2. 跨层级组件通信 三、性能优化实战 1. 惰性计算与缓存策略 2. 虚拟滚动与列表优化 3. Tree S…...

计算机网络(涵盖OSI,TCP/IP,交换机,路由器,局域网)

一、网络通信基础 &#xff08;一&#xff09;网络通信的概念 网络通信是指终端设备之间通过计算机网络进行的信息传递与交流。它类似于现实生活中的物品传递过程&#xff1a;数据&#xff08;物品&#xff09;被封装成报文&#xff08;包裹&#xff09;&#xff0c;通过网络…...

JVM-Java程序的运行环境

Java Virtual Machine Java程序的运行环境 JVM组成 程序计数器 线程私有的&#xff0c;内部保存的字节码的行号。用于记录正在执行的字节码指令的地址。 Java堆 线程共享的区域: 主要用来保存对象实例, 数组等, 当堆中没有内存空间可分配给实例也无法再扩展时, 则抛出OutOfMe…...

什么是网关,网关的作用是什么?网络安全零基础入门到精通实战教程!

1. 什么是网关 网关又称网间连接器、协议转换器&#xff0c;也就是网段(局域网、广域网)关卡&#xff0c;不同网段中的主机不能直接通信&#xff0c;需要通过关卡才能进行互访&#xff0c;比如IP地址为192.168.31.9(子网掩码&#xff1a;255.255.255.0)和192.168.7.13(子网掩码…...

makefile+LSF

LSF LSF&#xff08;Load Sharing Facility&#xff09;是一种常用的集群作业调度系统&#xff0c;bsub 命令用于提交作业到 LSF 集群&#xff0c;而若要关闭&#xff08;终止&#xff09;一个正在运行的作业&#xff0c;需要使用 bkill 命令&#xff0c;下面为你详细介绍相关…...

《千恋万花》无广版手游安卓苹果免费下载直装版

自取https://pan.xunlei.com/s/VOJS77k8NDrVawqcOerQln2lA1?pwdn6k8 《千恋万花》&#xff1a;柚子社的和风恋爱杰作 《千恋万花》&#xff08;Senren * Banka&#xff09;是由日本知名美少女游戏品牌柚子社&#xff08;Yuzusoft&#xff09;于2016年推出的一款和风恋爱题材…...

javaEE-14.spring MVC练习

目录 1.加法计算器 需求分析: 前端页面代码: 后端代码实现功能: 调整前端页面代码: 进行测试: 2.用户登录 需求分析: 定义接口: 1.登录数据校验接口: 2.查询登录用户接口: 前端代码: 后端代码: 调整前端代码: 测试/查错因 后端: 前端: lombok工具 1.引入依赖…...

rabbitmq五种模式的实现——springboot

rabbitmq五种模式的实现——springboot 基础知识和javase的实现形式可以看我之前的博客 代码地址&#xff1a;https://github.com/9lucifer/rabbitmq4j-learning 一、进行集成 &#xff08;一&#xff09;Spring Boot 集成 RabbitMQ 概述 Spring Boot 提供了对 RabbitMQ 的自…...

23. AI-大语言模型-DeepSeek赋能开发-Spring AI集成

文章目录 前言一、Spring AI 集成 DeepSeek1. 开发AI程序2. DeepSeek 大模型3. 集成 DeepSeek 大模型1. 接入前准备2. 引入依赖3. 工程配置4. 调用示例5. 小结 4. 集成第三方平台&#xff08;已集成 DeepSeek 大模型&#xff09;1. 接入前准备2. POM依赖3. 工程配置4. 调用示例…...

Educational Codeforces Round 174 (Rated for Div. 2)(ABCD)

A. Was there an Array? 翻译&#xff1a; 对于整数数组 ​&#xff0c;我们将其相等特征定义为数组 &#xff0c;其中&#xff0c;如果数组 a 的第 i 个元素等于其两个相邻元素&#xff0c;则 &#xff1b;如果数组 a 的第 i 个元素不等于其至少一个相邻元素&#xff0c;则 …...

如何在本机上模拟IP地址

如何在本机上模拟IP地址 前言 在某些开发或测试场景中&#xff0c;我们可能需要在本机上模拟一个指定的 IP 地址&#xff0c;并让局域网内的其他设备能够通过该 IP 访问本机提供的服务&#xff08;如 Web 服务&#xff09;。 本文将详细介绍如何在 Windows 和 macOS 系统上实…...

C++二叉树:数据的“家族树”与高效检索的奥秘

C二叉树&#xff1a;数据的“家族树”与高效检索的奥秘 开篇小故事&#xff1a;图书馆的“智能目录” 想象一座古老的图书馆&#xff0c;藏书百万&#xff0c;却能在几秒内找到任意一本书。 秘密在于它的“智能目录系统”——一本巨大的家族树状手册&#xff1a; 每本书按主题…...

深入解析 Vue 项目中的缓存刷新机制:原理与实战

目录 前言1. Demo2. 知识拓展 前言 在 Vue 项目中&#xff0c;缓存通常用于存储用户信息、角色权限、系统设置等&#xff0c;以提高页面加载速度并减少 API 请求 这里使用 web-storage-cache 作为封装的本地存储工具&#xff0c;支持 localStorage 和 sessionStorage 方式存储…...

【嵌入式Linux应用开发基础】进程间通信(1):管道

目录 一、管道的基本概念 二、管道的工作原理 三、管道的类型 3.1. 匿名管道&#xff08;Anonymous Pipe&#xff09; 3.2. 命名管道&#xff08;Named Pipe&#xff0c;FIFO&#xff09; 四、管道的读写规则 4.1. 匿名管道的读写规则 4.2. 命名管道的读写规则 五、管…...