手写简易RPC(实践版)
- 手机
- 2025-08-26 08:27:01

首先了解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{ @Override public 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请求 @Override protected 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(实践版)”