手写一个简单的RPC框架

学习RPC框架,由繁化简,了解其本质原理

文章目录

  • 项目简介
    • 什么是RPC?
    • 项目模块
  • 项目代码
    • common模块
    • client模块
    • server模块
    • framework模块
    • 测试

项目简介

什么是RPC?

RPC(Remote Procedure Call)即远程过程调用,不同于本地调用,RPC是指调用远端机器的函数或方法,且不需要关心底层的调用细节,如网络协议和传输协议等,对于调用者来说,和调用本地方法没有什么区别。

项目模块

  1. common模块:定义了用户接口和实体类User
  2. client模块:调用RPC框架的代理类,获取结果
  3. server模块:
  • 实现common的接口,把实现类注册到注册中心中
  • 调用RpcServer开启socket
  • 根据RpcRequest类的信息,获取到注册中心的实现类
  • 执行方法,返回结果,通过socket返回
  1. Rpc framework
  • 注册中心
  • RpcRequest,装载类的信息
  • RpcServer:创建socket,接受客户端的请求

在这里插入图片描述

项目代码

common模块

实体类和定义的接口

package com.rpc.common;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 10:37
 * @Desc: 网络中传输的信息
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User implements Serializable {

    private Integer id;

    private String name;

    private Integer age;

    private int sex;
}

package com.rpc.common;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 10:38
 * @Desc:
 */
public interface IUserService {
    User getById(Integer id);

    User getUsername(String userName);
}

client模块

package com.rpc.client;

import com.rpc.common.IUserService;
import com.rpc.framework.proxy.RpcProxy;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 11:39
 * @Desc:
 */
public class Client {
    public static void main(String[] args) {
        RpcProxy rpcProxy = new RpcProxy();
        IUserService productService = (IUserService) rpcProxy.remoteCall("localhost", 10000, IUserService.class);
        System.out.println("productService = " + productService.getById(10));
    }
}

server模块

package com.rpc.server;

import com.rpc.common.IUserService;
import com.rpc.framework.Registry;
import com.rpc.framework.RpcServer;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 11:37
 * @Desc:
 * https://www.cnblogs.com/fantongxue/p/16004920.html
 */
public class Server {
    /**
     * 把接口和实现类注册到RPC的注册中心,然后通过RPC的RPCServer开启一个serversocket,监听某一个端口。
     * @param args
     */
    public static void main(String[] args) {
        Registry.put(IUserService.class.getName(), UserServiceImpl.class);
        new RpcServer().provide(10000);
    }
}

package com.rpc.server;

import com.rpc.common.User;
import com.rpc.common.IUserService;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 11:35
 * @Desc:
 */
public class UserServiceImpl implements IUserService {
    @Override
    public User getById(Integer id) {
        return User.builder()
                .id(id)
                .name("yangmiao")
                .age(100)
                .sex(1)
                .build();
    }

    @Override
    public User getUsername(String userName) {
        return User.builder()
                .name(userName)
                .build();
    }
}

framework模块

package com.rpc.framework;

import java.util.HashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 10:39
 * @Desc: 注册中心
 */
public class Registry {
    private final static HashMap<String, Class> map = new HashMap<>();
    private final static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * 读缓存
     * @param key
     * @return
     */
    public static Class get(String key){
        // 读锁
        Lock writeLock = readWriteLock.writeLock();
        // 写锁
        Lock readLock = readWriteLock.readLock();

        Class v = null;
        readLock.lock();
        try {
            v = map.get(key);
        }finally {
            readLock.unlock();
        }
        if (v != null){
            return v;
        }
        // 缓存中不存在
        writeLock.lock();
        try {
            v = map.get(key);
            if (v==null){
                // 1.查询数据库
                // 2.写入缓存
                map.put(key,v);
            }
        }finally {
            writeLock.unlock();
        }
        return v;
    }

    /**
     * 写缓存
     * @param key
     * @param value
     * @return
     */
    public static Class put(String key, Class value){
        Lock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            return map.put(key,value);
        }finally {
            writeLock.unlock();
        }
    }

    public static boolean containsKey(String key){
        return map.containsKey(key);
    }

}
package com.rpc.framework;

import lombok.Data;

import java.io.Serializable;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 10:41
 * @Desc:
 */
@Data
public class RpcRequest implements Serializable {

    private String className;

    private String methodName;

    private Class[] types;

    private Object[] params;

}
package com.rpc.framework;


import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 10:42
 * @Desc:
 */
public class RpcServer {
    /**
     * 创建线程池
     */
    private ExecutorService executors = Executors.newFixedThreadPool(5);

    public void provide(int port){
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            while (true){
                Socket socket = serverSocket.accept();
                executors.execute(new ProcessHandler(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.rpc.framework;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 10:50
 * @Desc: 处理服务端逻辑
 */
public class ProcessHandler implements Runnable {
    private Socket socket;

    public ProcessHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        ObjectInputStream objectInputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            objectInputStream = new ObjectInputStream(socket.getInputStream());
            RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();

            Class clazz = null;
            // 判断是否存在于注册中心中
            if (Registry.containsKey(rpcRequest.getClassName())){
                clazz = Registry.get(rpcRequest.getClassName());
            }
            Method method = clazz.getMethod(rpcRequest.getMethodName(), rpcRequest.getTypes());
            Object result = method.invoke(clazz.newInstance(), rpcRequest.getParams());

            // 返回结果
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(result);
            objectOutputStream.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }finally {
            try {
                if (objectInputStream !=null){
                    objectInputStream.close();
                }
                if (objectOutputStream !=null){
                    objectOutputStream.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }

        }
    }
}

代理

package com.rpc.framework.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 11:18
 * @Desc: 创建动态代理
 */
public class RpcProxy<T> {

    public T remoteCall(String host,int port,Class clazz){
        return (T) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                (Class<?>[]) new Class[]{clazz},
                new RemoteInvocationHandler(host,port,clazz)
        );
    }
}
package com.rpc.framework.proxy;

import com.rpc.framework.RpcRequest;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @Author: Yangmiao
 * @Date: 2023/2/8 11:22
 * @Desc: 代理类执行的逻辑
 */
public class RemoteInvocationHandler implements InvocationHandler {

    private String host;
    private int port;
    private Class clazz;

    public RemoteInvocationHandler(String host,int port,Class clazz){
        this.host = host;
        this.port = port;
        this.clazz = clazz;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RpcRequest rpcRequest = new RpcRequest();

        rpcRequest.setClassName(clazz.getName());
        rpcRequest.setMethodName(method.getName());
        rpcRequest.setTypes(method.getParameterTypes());
        rpcRequest.setParams(args);

        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            Socket socket = new Socket(host,port);
            // 发送消息
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(rpcRequest);
            objectOutputStream.flush();

            // 接受结果
            objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object readObject = objectInputStream.readObject();
            return readObject;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if (objectInputStream !=null){
                    objectInputStream.close();
                }
                if (objectOutputStream!=null){
                    objectOutputStream.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        return null;
    }
}

测试

在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Cursor:GPT-4 驱动的强大代码编辑器

Cursor &#xff08;https://www.cursor.so/&#xff09;是 GPT-4 驱动的一款强大代码编辑器&#xff0c;可以辅助程序员进行日常的编码。下面通过一个实际的例子来展示 Cursor 如何帮助你编程。这个例子做的事情是网页抓取。抓取的目标是百度首页上的百度热搜&#xff0c;如下…

SWA Object Detection随机权重平均【论文+代码】

随机权重平均摘要IntroductionSWA实验部分消融实验摘要 您想在不增加推断成本和不改变检测器的情况下提高对象检测器的1.0 AP吗&#xff1f;让我们告诉您一个这样的秘方。这个秘方令人惊讶地简单&#xff1a;使用循环学习率训练您的检测器额外的12个epoches&#xff0c;然后将…

最强的Python可视化神器,你有用过么?

数据分析离不开数据可视化&#xff0c;我们最常用的就是Pandas&#xff0c;Matplotlib&#xff0c;Pyecharts当然还有Tableau&#xff0c;看到一篇文章介绍Plotly制图后我也跃跃欲试&#xff0c;查看了相关资料开始尝试用它制图。 1、Plotly Plotly是一款用来做数据分析和可视…

【数据结构】Java实现队列与循环队列

目录 1. 概念 2. 队列的使用 3. 自己动手实现队列 3.1 MyQueue接口 3.2 LinkedQueue类 3.3 入队列 3.4 出队列 3.5 获取队头元素 3.6 获取队列中有效元素个数与检测队列是否为空 3.7 toString方法 4. 整体实现 4.1 LinkedQueue类 4.2 Test类 4.3 测试结果 5. 循…

while实现1到100相加求和-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)

【案例2-7】while实现1到100相加求和 一、案例描述 考核知识点 while循环语句 练习目标 掌握while循环语句。 需求分析 1-100之间的数相加求和&#xff0c;本案例通过while循环语句来实现。 案例分析 效果如图2-10所示。1-100所有数的和 具体实现步骤如下&#xff1a; 在&l…

【进阶数据结构】——红黑树

&#x1f308;感谢阅读East-sunrise学习分享——[进阶数据结构]红黑树 博主水平有限&#xff0c;如有差错&#xff0c;欢迎斧正&#x1f64f;感谢有你 码字不易&#xff0c;若有收获&#xff0c;期待你的点赞关注&#x1f499;我们一起进步&#x1f680; &#x1f308;我们上一…

SpringCloud之 LoadBalancer和Feign负载均衡

文章目录LoadBalancer 负载均衡一、LoadBalanced 负载均衡&#x1f33d;①观察负载均衡现象&#x1f33d;②LoadBalanced 源码剖析二、自定义负载均衡三、OpenFeign 实现负载均衡&#x1f346;①添加依赖&#x1f346;②启动类添加 EnableFeignClients&#x1f346;③创建客户端…

MySQL的COUNT语句,竟然都能被面试官虐的这么惨!?

关于数据库中行数统计&#xff0c;无论是MySQL还是Oracle&#xff0c;都有一个函数可以使用&#xff0c;那就是COUNT 但是&#xff0c;就是这个常用的COUNT函数&#xff0c;却暗藏着很多玄机&#xff0c;尤其是在面试的时候&#xff0c;一不小心就会被虐。不信的话请尝试回答下…

一文了解Jackson注解@JsonFormat及失效解决

背景 项目中使用WRITE_DATES_AS_TIMESTAMPS: true转换日期格式为时间戳未生效。如下&#xff1a; spring:jackson:time-zone: Asia/Shanghaiserialization:WRITE_DATES_AS_TIMESTAMPS: true尝试是否关于时间的注解是否会生效&#xff0c;使用JsonForma和JsonFiled均失效。 常…

【Docker】CAdvisor+InfluxDB+Granfana容器监控

文章目录原生命令 docker stats容器监控3剑客CIGCAdvisorInfluxDBGranfanacompose容器编排&#xff0c;一套带走新建目录新建3件套组合的 docker-compose.yml检查配置&#xff0c;有问题才有输出 docker-compose config -q启动docker-compose文件 docker-compose up -d测试浏览…

HTML5 Canvas

HTML5 Canvas <canvas>元素是HTML5中的新元素&#xff0c;通过使用该元素&#xff0c;你可以在网页中绘制所需的图形。 标签定义图形&#xff0c;比如图表和其他图像&#xff0c;您必须使用脚本来绘制图形。在画布上&#xff08;Canvas&#xff09;画一个红色矩形&#…

Java基础知识之HashMap的使用

一、HashMap介绍 HashMap是Map接口的一个实现类&#xff08;HashMap实现了Map的接口&#xff09;&#xff0c;它具有Map的特点。HashMap的底层是哈希表结构。 Map是用于保存具有映射关系的数据集合&#xff0c;它具有双列存储的特点&#xff0c;即一次必须添加两个元素&#xf…

5个高清/4K视频素材网站,免费下载。

本期跟大家分享5个超好用的视频素材网站&#xff0c;4K质量&#xff0c;免费可商用。 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx 菜鸟图库主要提供设计素材为主&#xff0c;自媒体相关素材也很多&#xff0c;像商用图片、背景图、视频素材、音频素材都很齐…

算法刷题总结 (二) 回溯与深广搜算法

算法总结2 回溯与深广搜算法一、理解回溯算法1.1、回溯的概念1.2、回溯法的效率1.3、回溯法问题分类1.4、回溯法的做题步骤二、经典问题2.1、组合问题2.1.1、77. 组合 - 值不重复2.1.2、216.组合总和III - 值不重复且等于目标值2.1.3、17. 电话号码的字母组合 - 双层回溯2.1.4、…

KafKa知识汇总

前言 汇总相关知识 Kafka快速实战与基本原理详解

LeetCode:215. 数组中的第K个最大元素

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340;算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;215. 数组中的第K个最大元素 题目描述&#xff1a;给定整数数组nums和整…

Django 之 Cookie 和 Session

3. Cookie 和 Session 会话 因为 HTTP 协议是无状态的&#xff0c;每次浏览器请求 request都是无状态的&#xff0c;后台服务器无法识别当前请求与上一次请求及之后请求是否为同一用户。 对于静态网站来说无所谓&#xff08;所有用户看到的都是一样的&#xff09;&#xff0c…

【C++】引用详细解析

目录引用的概念引用的用法引用的特性常引用&#xff08;涉及权限的放大与缩小&#xff09;引用的使用场景**作参数****作返回值**正确使用引用返回传值、传引用效率比较引用和指针的区别引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0…

干货 | 开关电源的PCB布线设计技巧—如何降低EMI?

开关电源PCB排版是开发电源产品中的一个重要过程。许多情况下&#xff0c;一个在纸上设计得非常完美的电源可能在初次调试时无法正常工作&#xff0c;原因是该电源的PCB排版存在着许多问题。为了适应电子产品飞快的更新换代节奏&#xff0c;产品设计工程师更倾向于选择在市场上…

【Zblog建站】搭建属于自己的博客网站,并内网穿透实现公网访问

文章目录1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置4. 公网访问测试5. 结语1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕不开…