手写简易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-远程过程调用,openFeign,Dubbo都可以算作rpc,以微服务来具体说明,就是在本地不需要去发送请求,通过rpc框架,像调用本地方法一样调用其他服务的方法,本质上还是要经过网络&…...
mysql学习笔记-多版本并发控制
1、什么是ReadView 在 MVCC机制中,多个事务对同一个行记录进行更新会产生多个历史快照,这些历史快照保存在 Undo Log里。如果一个事务想要查询这个行记录,需要读取哪个版本的行记录呢?这时就需要用到 ReadView 了,它帮我们解决了…...
问题记录汇总
记录一些问题 如何分析错误帧问题-CSDN博客...
算法日记20:SC72最小生成树(prim朴素算法)
一、题目: 二、题解 2.1:朴素prim的步骤解析 O ( n 2 ) O(n^2) O(n2)(n<1e3) 0、假设,我们现在有这样一个有权图 1、我们随便找一个点,作为起点开始构建最小生成树(一般是1号),并且存入intree[]状态数组中…...
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 请求时,当期望返回的响应体为 JSON 格式,但实际响应体不符合 JSON 格式时出现的错误。这个…...
Redis7——基础篇(五)
前言:此篇文章系本人学习过程中记录下来的笔记,里面难免会有不少欠缺的地方,诚心期待大家多多给予指教。 基础篇: Redis(一)Redis(二)Redis(三)Redis&#x…...
spring boot知识点1
1.什么是spring boot spring boot是spring框架的子项目,主要特点是自动配置,以及内置的tomcat服务器,适合快速开发web与微服务架构 2.spring boot和spring cloud俩者之间的联系 spring boot可单独运行, spring cloud则是用于多…...
从零搭建微服务项目Base(第7章——微服务网关模块基础实现)
前言: 在前面6章的学习中已经完成了服务间的调用实现,即各微服务通过nacos或eureka服务器完成服务的注册,并从nacos中拉取配置实现热更新。当某个服务接口需要调用其他服务时,通过feign定义接口,并通过注解配置服务名…...
pdf转换成word在线 简单好用 支持批量转换 效率高 100%还原
pdf转换成word在线 简单好用 支持批量转换 效率高 100%还原 在数字化办公的浪潮中,文档格式转换常常让人头疼不已,尤其是 PDF 转 Word 的需求极为常见。PDF 格式虽然方便阅读和传输,但难以编辑,而 Word 格式却能灵活地进行内容修…...
嵌入式音视频开发(二)ffmpeg音视频同步
系列文章目录 嵌入式音视频开发(零)移植ffmpeg及推流测试 嵌入式音视频开发(一)ffmpeg框架及内核解析 嵌入式音视频开发(二)ffmpeg音视频同步 嵌入式音视频开发(三)直播协议及编码器…...
SpringBoot速成概括
视频:黑马程序员SpringBoot3Vue3全套视频教程,springbootvue企业级全栈开发从基础、实战到面试一套通关_哔哩哔哩_bilibili 图示:...
微信小程序image组件mode属性详解
今天学习微信小程序开发的image组件,mode属性的属性值不少,一开始有点整不明白。后来从网上下载了一张图片,把每个属性都试验了一番,总算明白了。现总结归纳如下: 1.使用scaleToFill。这是mode的默认值,sc…...
Matlab写入点云数据到Rosbag
最近有需要读取一个点云并做处理后,重新写回rosbag。网上有很多读取的教程,但没有写入。自己写入时也遇到了很多麻烦,踩了一堆坑进行记录。 1. rosbag中一个lidar的msg有哪些信息? 通过如下代码,先读取一个rosbag的l…...
数据分析--数据清洗
一、数据清洗的重要性:数据质量决定分析成败 1.1 真实案例警示 电商平台事故:2019年某电商大促期间,因价格数据未清洗导致错误标价,产生3000万元损失医疗数据分析:未清洗的异常血压值(如300mmHgÿ…...
iNeuOS工业互联网操作系统,民爆远程运维平台案例
iNeuOS工业互联网操作系统,民爆远程运维平台案例 目 录 1. 概述... 2 2. iNeuOS在民爆生产厂区和北京运维中心配置... 3 1.1 生产厂区配置... 3 1.2 运维中心配置... 7 1. 概述 针对本项目进行初步调研,项目的总体需求为满足新建…...
用命令模式设计一个JSBridge用于JavaScript与Android交互通信
用命令模式设计一个JSBridge用于JavaScript与Android交互通信 在开发APP的过程中,通常会遇到Android需要与H5页面互相传递数据的情况,而Android与H5交互的容器就是WebView。 因此要想设计一个高可用的 J S B r i d g e JSBridge JSBridge,不…...
Vue 3最新组件解析与实践指南:提升开发效率的利器
目录 引言 一、Vue 3核心组件特性解析 1. Composition API与组件逻辑复用 2. 内置组件与生命周期优化 3. 新一代UI组件库推荐 二、高级组件开发技巧 1. 插件化架构设计 2. 跨层级组件通信 三、性能优化实战 1. 惰性计算与缓存策略 2. 虚拟滚动与列表优化 3. Tree S…...
计算机网络(涵盖OSI,TCP/IP,交换机,路由器,局域网)
一、网络通信基础 (一)网络通信的概念 网络通信是指终端设备之间通过计算机网络进行的信息传递与交流。它类似于现实生活中的物品传递过程:数据(物品)被封装成报文(包裹),通过网络…...
JVM-Java程序的运行环境
Java Virtual Machine Java程序的运行环境 JVM组成 程序计数器 线程私有的,内部保存的字节码的行号。用于记录正在执行的字节码指令的地址。 Java堆 线程共享的区域: 主要用来保存对象实例, 数组等, 当堆中没有内存空间可分配给实例也无法再扩展时, 则抛出OutOfMe…...
什么是网关,网关的作用是什么?网络安全零基础入门到精通实战教程!
1. 什么是网关 网关又称网间连接器、协议转换器,也就是网段(局域网、广域网)关卡,不同网段中的主机不能直接通信,需要通过关卡才能进行互访,比如IP地址为192.168.31.9(子网掩码:255.255.255.0)和192.168.7.13(子网掩码…...
makefile+LSF
LSF LSF(Load Sharing Facility)是一种常用的集群作业调度系统,bsub 命令用于提交作业到 LSF 集群,而若要关闭(终止)一个正在运行的作业,需要使用 bkill 命令,下面为你详细介绍相关…...
《千恋万花》无广版手游安卓苹果免费下载直装版
自取https://pan.xunlei.com/s/VOJS77k8NDrVawqcOerQln2lA1?pwdn6k8 《千恋万花》:柚子社的和风恋爱杰作 《千恋万花》(Senren * Banka)是由日本知名美少女游戏品牌柚子社(Yuzusoft)于2016年推出的一款和风恋爱题材…...
javaEE-14.spring MVC练习
目录 1.加法计算器 需求分析: 前端页面代码: 后端代码实现功能: 调整前端页面代码: 进行测试: 2.用户登录 需求分析: 定义接口: 1.登录数据校验接口: 2.查询登录用户接口: 前端代码: 后端代码: 调整前端代码: 测试/查错因 后端: 前端: lombok工具 1.引入依赖…...
rabbitmq五种模式的实现——springboot
rabbitmq五种模式的实现——springboot 基础知识和javase的实现形式可以看我之前的博客 代码地址:https://github.com/9lucifer/rabbitmq4j-learning 一、进行集成 (一)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. 集成第三方平台(已集成 DeepSeek 大模型)1. 接入前准备2. POM依赖3. 工程配置4. 调用示例…...
Educational Codeforces Round 174 (Rated for Div. 2)(ABCD)
A. Was there an Array? 翻译: 对于整数数组 ,我们将其相等特征定义为数组 ,其中,如果数组 a 的第 i 个元素等于其两个相邻元素,则 ;如果数组 a 的第 i 个元素不等于其至少一个相邻元素,则 …...
如何在本机上模拟IP地址
如何在本机上模拟IP地址 前言 在某些开发或测试场景中,我们可能需要在本机上模拟一个指定的 IP 地址,并让局域网内的其他设备能够通过该 IP 访问本机提供的服务(如 Web 服务)。 本文将详细介绍如何在 Windows 和 macOS 系统上实…...
C++二叉树:数据的“家族树”与高效检索的奥秘
C二叉树:数据的“家族树”与高效检索的奥秘 开篇小故事:图书馆的“智能目录” 想象一座古老的图书馆,藏书百万,却能在几秒内找到任意一本书。 秘密在于它的“智能目录系统”——一本巨大的家族树状手册: 每本书按主题…...
深入解析 Vue 项目中的缓存刷新机制:原理与实战
目录 前言1. Demo2. 知识拓展 前言 在 Vue 项目中,缓存通常用于存储用户信息、角色权限、系统设置等,以提高页面加载速度并减少 API 请求 这里使用 web-storage-cache 作为封装的本地存储工具,支持 localStorage 和 sessionStorage 方式存储…...
【嵌入式Linux应用开发基础】进程间通信(1):管道
目录 一、管道的基本概念 二、管道的工作原理 三、管道的类型 3.1. 匿名管道(Anonymous Pipe) 3.2. 命名管道(Named Pipe,FIFO) 四、管道的读写规则 4.1. 匿名管道的读写规则 4.2. 命名管道的读写规则 五、管…...
