手写简易RPC(实践版)

ops/2025/2/21 4:58:38/

首先了解rpc

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

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

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

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

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

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

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

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

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

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

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

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

java">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中添加主函数

java">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的方法,如下

java">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对象,必须要实现序列化

java">@Data
@AllArgsConstructor
public class RpcRequest implements Serializable {//序列化很重要/*** 服务名称*/private String serviceName;/*** 方法名称*/private String methodName;/*** 参数类型列表*/private Class<?>[] parameterTypes;/*** 参数列表*/private Object[] args;}
java">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消费就可以了

java">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中如下

java">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版本

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

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

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

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


http://www.ppmy.cn/ops/160150.html

相关文章

Unity之Serialized序列化:从原理到实践

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之Serialized序列化&#xff1a;从原理到实践 TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心…

uniapp录制语音

给大家讲解瞎 录制语音 的功能&#xff0c;这部分主要涉及到以下几个步骤&#xff1a;开始录音、停止录音、播放录音的功能 1.开始录音 (startRecording 函数) 当用户点击 开始录音 按钮时&#xff0c;调用 startRecording 函数开始录音。录音通过 uni.getRecorderManager() …

人工智能(AI)的不同维度分类

人工智能(AI)的分类 对机器学习进行分类的方式多种多样&#xff0c;可以根据算法的特性、学习方式、任务类型等不同维度进行分类这些分类都不是互斥的&#xff1a; 1、按数据模态不同:图像&#xff0c;文本&#xff0c;语音&#xff0c;多态等 2、按目标函数不同:判别式模型…

AIGC视频生成明星——Emu Video模型

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍Meta的视频生成模型Emu Video&#xff0c;作为Meta发布的第二款视频生成模型&#xff0c;在视频生成领域发挥关键作用。 &#x1f33a;优质专栏回顾&am…

DeepSeek-R1本地部署详细指南!(Ollama+Chatbox AI+Open WebUI)

一、前言 DeepSeek&#xff08;深度求索&#xff09;是一家中国人工智能企业&#xff0c;其在人工智能领域取得了显著成果&#xff0c;特别是其发布的新一代大模型DeepSeek-R1和DeepSeek-V3&#xff0c;受到了广泛关注。 DeepSeek官网&#xff1a;https://www.deepseek.com/ …

LLaMA-Factory DeepSeek-R1 模型 微调基础教程

LLaMA-Factory 模型 微调基础教程 LLaMA-FactoryLLaMA-Factory 下载 AnacondaAnaconda 环境创建软硬件依赖 详情LLaMA-Factory 依赖安装CUDA 安装量化 BitsAndBytes 安装可视化微调启动 数据集准备所需工具下载使用教程所需数据合并数据集预处理 DeepSeek-R1 可视化微调数据集处…

九联UNT403AS_晶晨S905L3S芯片_2+8G_安卓9.0_卡刷固件包

九联UNT403AS_晶晨S905L3S芯片_28G_安卓9.0_卡刷固件包 刷机教程&#xff1a; 下载好引导文件和固件&#xff0c;8G以下U盘格式化&#xff0c;将文件放入U盘根目录&#xff0c;用牙签抵着复位孔同时开机&#xff0c;5秒后松开即可进去刷机模式 刷机固件&#xff1a; 链接: h…

深度学习02 神经网络实现手写数字案例

目录 下载手写数字图像(图像标签) 展示手写数字图片 数据打包 判断当前设备是否支持GPU 建立神经网络模型 设置训练集与测试集 创建损失函数、优化器 开始训练 下载手写数字图像(图像标签) training_datadatasets.MNIST(rootdata,trainTrue,downloadTrue,transformToTe…