手写简易RPC框架

news2025/1/6 18:12:55

目录

简介

服务提供者

服务注册:注册中心

HttpServerHandler处理远程调用请求

consumer服务消费端


简介

RPC(Remote Procedure Call)——远程过程调用,它是一种通过网络从远程计算机程序上请求服务, 而不需要了解底层网络技术的协议,在面向对象的编程语言中,远程过程调用即是远程方法调用

基本实现思路如下:

项目结构:

  • provider服务提供
  • consumer服务消费
  • registry注册
  • protocol协议

服务提供者

  • 定义服务接口

接口HelloService

public interface HelloService {
 String sayHello(String message);
}
  • 实现类HelloServiceImpl
public class HelloServiceImpl implements HelloService {
 @Override
 public String sayHello(String name) {
        
        return name+ "调用了myRPC的服务";
    }
}

服务注册:注册中心

此处注册中心我们将服务注册在map集合中,结构:Map<String,Map<URL,Class>> 外边map的key存储 服务接口的全类名,URL封装了调用服务的ip和port,里边value指定指定具体实现类 注册中心类提供注册服务并暴露服务和发现服务功能:

public class URL {
​
    private String hostname;
    private Integer port;
    
    @Override
    public boolean equals(Object obj) {
        if(obj==null){
            return false;
        }
        if(!(obj instanceof  URL)){
            return false;
        }
        URL url = (URL) obj;
        if(hostname.equals(((URL) obj).getHostname())  && port.intValue() == url.port.intValue()){
            return true;
        }
        return false;
    }
​
    @Override
    public int hashCode() {
        return hostname.hashCode();
    }
}
​
public class NativeRegistry {
​
​
    private static Map<String, Map<URL,Class>> registCenter = new HashMap<>();
​
​
    /**
     * 注册服务
     * @param url
     * @param interfaceName
     * @param implClass
     */
    public static void regist(URL url,String interfaceName,Class implClass){
​
        Map<URL,Class> map = new HashMap<>();
        map.put(url,implClass);
        registCenter.put(interfaceName,map);
    }
​
    /**
     * 从注册中心获取服务
     * @param url
     * @param interfaceName
     * @return
     */
    public static Class get(URL url,String interfaceName){
        return registCenter.get(interfaceName).get(url);
    }
​
​
}
  • 注册服务
public class ServiceProvider {
​
    public static void main(String[] args) {
​
        //创建URL
        URL url = new URL("localhost", 8080);
​
        //注册中心中注册服务
        NativeRegistry.regist(url, HelloService.class.getName(), HelloServiceImpl.class);
​
        //启动并暴露服务
        HttpServer httpServer = new HttpServer();
        httpServer.start(url.getHostname(),url.getPort());
​
    }
}
  • 暴露服务

服务之间调用的通信协议采用http协议,所以在服务provider中启动tomcat暴露服务

添加内嵌tomcat的依赖

  <!--内嵌tomcat-->
    <dependencies>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.12</version>
        </dependency>
    </dependencies>
  • 创建HttpServer
public class HttpServer {
​
​
    /**
     * tomcat服务启动
     * 参考tomcat配置
     * <Server port="8005" shutdown="SHUTDOWN">
     *  <Service name="Catalina">
     *      <Connector port="8080" protocol="HTTP/1.1"
     *                connectionTimeout="20000"
     *                redirectPort="8443"
     *         URIEncoding="UTF-8"/>
     *      <Engine name="Catalina" defaultHost="localhost">
     *          <Host name="localhost"  appBase="webapps"
     *              unpackWARs="true" autoDeploy="true">
     *              <Context path="" doBase="WORKDIR" reloadable="true"/>
     *              </Host>
     *      </Engine>
     *   </Service>
     * </Server>
     */
​
​
    /**
     * 启动服务
     * @param hostname
     * @param port
     */
    public void start(String hostname,int port){
        // 实例一个tomcat
        Tomcat tomcat = new Tomcat();
​
        // 构建server
        Server server = tomcat.getServer();
​
        // 获取service
        Service service = server.findService("Tomcat");
​
        // 构建Connector
        Connector connector = new Connector();
        connector.setPort(port);
        connector.setURIEncoding("UTF-8");
​
        // 构建Engine
        Engine engine = new StandardEngine();
        engine.setDefaultHost(hostname);
​
        // 构建Host
        Host host = new StandardHost();
        host.setName(hostname);
​
        // 构建Context
        String contextPath = "";
        Context context = new StandardContext();
        context.setPath(contextPath);
        context.addLifecycleListener(new Tomcat.FixContextListener());// 生命周期监听器
​
        // 然后按照server.xml,一层层把子节点添加到父节点
        host.addChild(context);
        engine.addChild(host);
        service.setContainer(engine);
        service.addConnector(connector);
        // service在getServer时就被添加到server节点了
​
        // tomcat是一个servlet,设置路径与映射
        tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet());
        context.addServletMappingDecoded("/*","dispatcher");
​
        try {
            tomcat.start();// 启动tomcat
            tomcat.getServer().await();// 接受请求
        }catch (LifecycleException e){
            e.printStackTrace();
        }
    }
}
​
  • DispatcherServlet
public class DispatcherServlet extends HttpServlet {
​
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        new HttpServerHandler().handle(req,resp);
    }
}

HttpServerHandler处理远程调用请求

public class HttpServerHandler {
​
​
    /**
     *  服务的处理
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void handle(HttpServletRequest req, HttpServletResponse resp){
        try {
            //服务请求的处理逻辑
​
            //1 通过请求流获取请求服务调用的参数
            InputStream inputStream = req.getInputStream();
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
​
            Invocation invocation = (Invocation) objectInputStream.readObject();
​
            //2 从注册中心获取服务的列表
            Class implCass = NativeRegistry.get(new URL("localhost", 8080), invocation.getInterfaceName());
​
            //3 调用服务 反射
            Method method = implCass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
​
            String result = (String) method.invoke(implCass.newInstance(), invocation.getParams());
​
            //4 结果返回
            IOUtils.write(result,resp.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
​
​
    }
​
}
  • 封装调用参数Invocation
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Invocation implements Serializable {
​
    private String interfaceName;
    private String methodName;
    private Object[] params;
    private Class[] paramTypes;
​
}
  • 启动服务
public class ServiceProvider {
​
    public static void main(String[] args) {
​
        //创建URL
        URL url = new URL("localhost", 8080);
​
        //注册中心中注册服务
        NativeRegistry.regist(url, HelloService.class.getName(), HelloServiceImpl.class);
​
        //启动并暴露服务
        HttpServer httpServer = new HttpServer();
        httpServer.start(url.getHostname(),url.getPort());
​
    }
}

consumer服务消费端

  • 封装HttpClient对象,发起远程调用
public class HttpClient {
​
    /**
     * 远程方法调用
     * @param hostname :远程主机名
     * @param port :远程端口号
     * @param invocation :封装远程调用的信息
     */
    public String post(String hostname, int port, Invocation invocation) {
​
​
        try {
            URL url = new URL("http", hostname, port, "/client/");
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);// 必填项
​
            //发送调用的信息
            OutputStream os = connection.getOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(os);
            oos.writeObject(invocation);
            oos.flush();
            oos.close();
​
            // 将输入流转为字符串(此处可是java对象) 获取远程调用的结果
            InputStream is = connection.getInputStream();
            return IOUtils.toString(is);
​
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
​
    }
​
}
  • 调用测试
public class Consumer {
    
    public static void main(String[] args) {
​
        //封装一个invocation
        Invocation invocation = new Invocation(HelloService.class.getName(), "sayHello2",
                new Object[]{"Test"}, new Class[]{String.class});
​
        //远程调用服务
        String result = new HttpClient().post("localhost", 8080, invocation);
​
        System.out.println("远程调用执行的结果result="+result);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/542067.html

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

相关文章

我在VScode学Java(Java二维数组)

我的个人博客主页&#xff1a;如果\真能转义1️⃣说1️⃣的博客主页 关于Java基本语法学习---->可以参考我的这篇博客&#xff1a;(我在Vscode学Java) 接上回Java一维数组的介绍&#xff08;我在VScode学Java(Java一维数组&#xff09; &#xff09; 二维数组是Java中的一…

MySQL:数据库的约束

目录 1.数据库约束 1.1 非空&#xff1a;not null 1.2 唯一&#xff1a;unique ​​​​​​​ 1.3 默认值&#xff1a;default 1.4 列描述&#xff1a;comment 1.5 主键约束&#xff1a;primary key 1.6 外键约束 1.7 综合案例 2.插入查询结果 3.聚合函数 4.group by…

AI心理咨询师:舒缓焦虑,解放压力的秘诀

在如今高压力的生活和工作环境下&#xff0c;焦虑和内耗成为了越来越多人的问题。这一现象对我们的身体和心理都会造成很大的影响。如何治愈我们的焦虑和精神内耗&#xff1f; 1.减少工作压力 - 了解和认可自己的能力和限制&#xff0c;不要让工作压力压垮自己。 - 适当的规…

软件测试工程师的职业发展方向

一、软件测试工程师大致有4个发展方向: 1 资深软件测试工程师 一般情况&#xff0c;软件测试工程师可分为测试工程师、高级测试工程师和资深测试工程师三个等级。 达到这个水平比较困难&#xff0c;这需要了解很多知识&#xff0c;例如C语言&#xff0c;JAVA语言&#xff0c…

浙大数据结构

题目详情&#xff1a;06-图1 列出连通集 给定一个有N个顶点和E条边的无向图&#xff0c;请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时&#xff0c;假设我们总是从编号最小的顶点出发&#xff0c;按编号递增的顺序访问邻接点。 输入格式: 输入第1…

<数据结构>NO5.栈和队列

目录 栈 Ⅰ.栈的概念 Ⅱ.栈的实现 Ⅲ.测试代码 队列 Ⅰ.队列的概念 Ⅱ.队列的实现 &#x1f4ad;前言 栈和队列也是一种常见的线性存储的数据结构&#xff0c;只不过它们的某些操作受到了限制&#xff0c;比如栈只允许从栈顶插入删除元素、队列只允许从队尾插入元素&…

[算法] ArrayList 和 LinkedList 的优缺点比较及使用场景

&#x1f61a;一个不甘平凡的普通人&#xff0c;致力于为Golang社区和算法学习做出贡献&#xff0c;期待您的关注和认可&#xff0c;陪您一起学习打卡&#xff01;&#xff01;&#xff01;&#x1f618;&#x1f618;&#x1f618; &#x1f917;专栏&#xff1a;算法学习 &am…

MySQL高级_第12章_数据库其它调优策略

MySQL高级_第12章_数据库其它调优策略 1. 数据库调优的措施 1.1 调优的目标 尽可能 节省系统资源 &#xff0c;以便系统可以提供更大负荷的服务。&#xff08;吞吐量更大&#xff09; 合理的结构设计和参数调整&#xff0c;以提高用户操作 响应的速度 。&#xff08;响应速…

使用Visual Studio进行cuda编程配置环境四大坑(附解决方案)

写在前面&#xff0c;用于没有使用过Visual Studio进行cuda编程的同学看&#xff0c;以免在安装环境的时候踩到坑 第一坑&#xff1a;CUDA版本与NVIDIA显卡版本不匹配问题: 安装cuda版本坑&#xff0c;强烈建议看下自己的显卡支持什么版本的cuda&#xff0c;切记不要用最新版…

由浅入深Netty基础知识NIO网络编程

目录 1 非阻塞 vs 阻塞1.1 阻塞1.2 非阻塞1.3 多路复用 2 Selector2.1 创建2.2 绑定 Channel 事件2.3 监听 Channel 事件2.4 select 何时不阻塞 3 处理 accept 事件3.1 事件发生后能否不处理 4 处理 read 事件4.1 为何要 iter.remove()4.2 cancel 的作用4.3 不处理边界的问题4.…

Python 迭代器与生成器

一. 迭代器 迭代是Python最强大的功能之一&#xff0c;是访问集合元素的一种方式。 &#xff08;1&#xff09;迭代器对象从集合的第一个元素开始访问&#xff0c;直到所有的元素被访问完结束。 &#xff08;2&#xff09;迭代器可以记住遍历的位置的对象&#xff0c;并且只能…

Springboot 整合 spring security,简单实现认证和授权

一.简介 Spring Security 是 Spring 家族中的一个安全管理框架。 一般来说&#xff0c;常见的安全管理技术栈的组合是这样的&#xff1a; SSM ShiroSpring Boot/Spring Cloud Spring Security 对于一个权限管理框架而言&#xff0c;无论是 Shiro 还是 Spring Security&am…

tf.image.decode_jpeg(别名tf.io.decode_jpeg)函数工作原理分析

1 问题提出 最近在阅读某个论文的源代码时&#xff0c; 发现作者在读取图像数据时没有使用PIL.Image或opencv库&#xff0c;而是使用了tf.image.decode_jpeg&#xff0c;代码节选如下&#xff1a; # tf1中的函数, 用于读取文件 # tf2中该函数更改为了tf.io.read_file image_c…

【Linux权限的概念及理解】

目录 一、认识linux下的用户分类二、什么叫权限三、没有权限会怎么样&#xff08;见一见&#xff09;四、权限的修改问题4.1chmod指令4.2chown指令4.3chgrp指令 五、两个问题粘滞位5.1Question15.2Question25.3粘滞位 一、认识linux下的用户分类 Linux下有两种用户&#xff1a…

mysql中的Redo log

目录标题 前言redolog保证持久性redolog工作过程 redo log中的WAL&#xff08;先写日志&#xff0c;再写磁盘【写入redo log file】&#xff09;刷盘策略 重要参数innodb_flush_log_at_trx_commit如何选择 Redo log file日志文件日志文件组redo log刷盘与数据页刷盘redo log何时…

Vue CLI Todo-List案例

3.7. Todo-List 案例 组件化编码流程 拆分静态组件&#xff1a;组件要按照功能点拆分&#xff0c;命名不要与html元素冲突实现动态组件&#xff1a;考虑好数据的存放位置&#xff0c;数据是一个组件在用&#xff0c;还是一些组件在用 一个组件在用&#xff1a;放在组件自身即…

Cesium入门之八:Cesium加载矢量数据

目录 一、什么是矢量数据二、Cesium支持的矢量数据格式KML格式KmlDataSource CZML格式CzmlDataSource GeoJSON格式GeoJsonDataSource 三、Cesium加载GeoJSON数据格式的中国地图示例 一、什么是矢量数据 矢量数据是用于描述地理空间几何特征的一类基于向量的地理信息数据&#…

RabbitMQ的几种通讯方式及其代码示例

文章目录 一、引言二、RabbitMQ介绍三、RabbitMQ安装四、RabbitMQ架构4.1 官方的简单架构图4.2 RabbitMQ的完整架构图4.3 RabbitMQ 通讯方式4.4 Hello-World案例演示4.5 基本原理 五、SpringBoot整合RabbitMQ的使用5.1 导入依赖5.2 在application.properties中增加配置5.3 Hell…

前端学习--Vue(2)

一、Vue简介 1.1 概念 Vue是一套用于构建用户界面的前端框架 框架&#xff1a;现成解决方案&#xff0c;遵守规范去编写业务功能 指令、组件、路由、Vuex、vue组件库 1.2 特性 数据驱动视图 vue连接页面结构和数据&#xff0c;监听数据变化&#xff0c;自动渲染页面结构…

Vue--》Vue 3 路由进阶——从基础到高级的完整指南

目录 Vue3中路由讲解与使用 路由的安装与使用 路由模式的使用 编程式路由导航 路由传参 嵌套路由 命名视图 重定向与别名 Vue3中路由讲解与使用 Vue 路由是 Vue.js 框架提供的一种机制&#xff0c;它用于管理网页上内容的导航。Vue 路由可以让我们在不刷新页面的情况下…