使用 Socket和动态代理以及反射 实现一个简易的 RPC 调用

news2024/9/21 22:56:47

使用 Socket、动态代理、反射 实现一个简易的 RPC 调用


我们前面有一篇 socket 的文章,再之前,还有一篇 java动态代理的文章,本文用到了那两篇文章中的知识点,需要的话可以回顾一下。


下面正文开始:

我们的背景是一个名为cuihua-snack(翠花小吃店)的客户端,要调用cuihua-core(翠花核心厨房)的服务端的接口,要求采用RPC的方式调用。
首先,我们创建两个项目cuihua-snack、cuihua-core
在这里插入图片描述

在这里插入图片描述
其中,cuihua-core 包含两个模块(关于 maven的模块化开发 可以再去了解一下),core-api中定义了客户端与服务端共同需要的接口和参数;core-service 则是服务端的主要逻辑。

我们有这样一张系统调用关系示意图。
在这里插入图片描述
① 服务端启动socket服务;
② 客户端开始调用,通过代理的方式调用;
③ 代理中包含 socket 客户端,与服务端建立连接后,将请求接口的对象信息封装后进行序列化。
④ 服务端接收到 socket 请求后,反序列化拿到请求的对象信息。
⑤ 通过反射,调用接口实现类的方法并返回结果。
⑥ 服务端对执行结果序列化并通过socket 返回给客户端。
⑦ 客户端对服务端返回的数据进行反序列化后,输出。
以上七个步骤,就是整个简易RPC的调用过程。


下面我们来看代码:
首先看 core-api 中的代码:
在这里插入图片描述

/**
 * 上菜服务
 */
public interface DishService {
    String servePickedChineseCabbage(RequestDTO dto);
}
/**
 * 上菜requestDTO
 */
@Data
public class RequestDTO implements Serializable {
    /**
     * 菜量
     */
    private String quantityType;//big;medium;small;
    /**
     * 是否加辣
     */
    private boolean spicy;
    /**
     * 冷热
     */
    private String coldOrHot;//cold/hot
}
/**
 * RPC Request
 */

@Data
public class RpcRequest implements Serializable {
    private String className;
    private String methodName;
    private Object[] args;
    private Class[] types;

}

再来看 core-service的代码:
在这里插入图片描述

public class DishServiceImpl implements DishService {
    public String servePickedChineseCabbage(RequestDTO dto) {
        System.out.println(dto);
        return "Please wait a moment! The dish you want will come soon!";
    }
}
public class RpcService {
    private DishService service;
    public RpcService(DishService service){
        this.service = service;
    }
    public void serverRun() throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        ServerSocket serverSocket = new ServerSocket(8000);
        while(true) {
            Socket socket = serverSocket.accept();
            handler(socket);
        }

    }

    private void handler(Socket socket) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        //读取 socket 传输的数据
        ObjectInputStream objectinputstrem = new ObjectInputStream(socket.getInputStream());
        RpcRequest rpcRequest = (RpcRequest) objectinputstrem.readObject();
        String result = (String) this.invoke(rpcRequest);
        //将结果写回 socket
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        objectOutputStream.writeObject(result);
        objectOutputStream.flush();
    }

    private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //通过反射进行服务的调用
        Class clazz=Class.forName(request.getClassName());
        //找到目标方法
        Method method=clazz.getMethod(request.getMethodName(),request.getTypes());
        return method.invoke(service,request.getArgs());
    }
}
public class App 
{
    public static void main( String[] args ) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        DishService dishService = new DishServiceImpl();
        RpcService rpcService = new RpcService(dishService);
        rpcService.serverRun();
    }
}

最后,我们来看 cuihua-snack的代码:
在这里插入图片描述

public class CuihuaInvocationHandler implements InvocationHandler {

    private String host;
    private int port;
    public CuihuaInvocationHandler(String host,int port){
        this.host = host;
        this.port = port;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //开启客户端
        SocketClient socketClient = new SocketClient(host,port);
        //请求参数
        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setArgs(args);
        rpcRequest.setTypes(method.getParameterTypes());
        rpcRequest.setClassName(method.getDeclaringClass().getName());
        rpcRequest.setMethodName(method.getName());
        //发送
        Object obj = socketClient.transMessage(rpcRequest);

        //响应结果
        return obj;
    }
}
/**
 * 代理服务
 */
public class ProxyDishService {

    public  <T> T  getInstance(Class<T> clazz){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class<?>[]{clazz},new CuihuaInvocationHandler("localhost",8000));
    }
}
public class SocketClient {
    private String host;
    private int port;

    public SocketClient(String host,int port){
        this.host = host;
        this.port = port;
    }

    public Socket getSocket() throws IOException {
        Socket socket = new Socket(host,port);
        return socket;
    }

    public Object transMessage(RpcRequest request){
        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            Socket socket = getSocket();
            //将RpcRequest 写入到输出流中,通过socket传递给服务端
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(request);
            objectOutputStream.flush();

            //通过输入流,读取服务端通过socket返回的结果
            objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object obj = objectInputStream.readObject();
            return obj;
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try {
                objectInputStream.close();
                objectOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
public class App 
{
    public static void main( String[] args )
    {
        //调用代理
        ProxyDishService proxy = new ProxyDishService();
        RequestDTO dto = new RequestDTO();
        dto.setColdOrHot("hot");
        dto.setSpicy(true);
        dto.setQuantityType("big");
        String responseStr = proxy.getInstance(DishService.class).servePickedChineseCabbage(dto);
        System.out.println("响应结果:"+responseStr);

    }
}

执行 cuihua-core 中 core-service 下的 App 的main方法,启动 ServerSocket;
然后,执行cuihua-snack 中 App 的 main方法,socket客户端发起调用。
服务端打印日志如下:

RequestDTO(quantityType=big, spicy=true, coldOrHot=hot)

客户端打印日志如下:

响应结果:Please wait a moment! The dish you want will come soon!

以上就是 《使用 Socket和动态代理以及反射 实现一个简易的 RPC 调用》的全部内容,感谢阅读!

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

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

相关文章

掌握Rust:函数、闭包与迭代器的综合运用

掌握Rust&#xff1a;函数、闭包与迭代器的综合运用 引言&#xff1a;解锁 Rust 高效编程的钥匙函数定义与模式匹配&#xff1a;构建逻辑的基石高阶函数与闭包&#xff1a;代码复用的艺术迭代器与 for 循环&#xff1a;高效数据处理的引擎综合应用案例&#xff1a;构建一个简易…

最新App崩溃率出炉!这样的行业均值水平如何?

前不久发布的《2024 Q1 移动应用性能体验报告》(以下简称报告),公布了最新的App崩溃率行业均值。基于友盟覆盖的终端设备,观测启动次数和崩溃次数,《报告》综合计算得出iOS APP崩溃率0.21%,Android Java崩溃率0.22%、native 0.16%、ANR 0.53%。 作为国内领先的第三方全域数据智…

PyMol在Windows系统上的免费安装指南

PyMOL是一个强大的分子可视化工具&#xff0c;广泛应用于生物化学、分子生物学和材料科学等领域。对于需要在Windows系统上进行分子结构分析和可视化的用户来说&#xff0c;安装一个免费版本的PyMol至关重要。本文将提供详细的步骤&#xff0c;指导如何在Windows系统上免费安装…

有哪些好用的 AI 学术研究工具和科研工具?

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量https://aitools.jurilu.com/ AI 应用其实分两个层面&#xff0c;第一是模型&#xff0c;第二是应用。现在很多模型厂家都是既做 toC 的对话应用&#xff0c;也做 t…

Jmeter性能测试进行参数化操作

在使用Jmeter进行性能测试中,Jmeter的基本操作是肯定要会的,除此之外,还需要会多并发压测配置线程,多用户并发参数化的设置等等技术.下面就给大家介绍一下这个方面的内容: 一.Jmeter单个请求部署 1.设置线程组 注意:如果要使用调度器,那么循环次数的”永远”的选项一定要记得…

MySQL-视图、存储过程和触发器

一、视图的定义和使用 视图是从一个或者几个基本表&#xff08;或视图&#xff09;导出的表。它与基本表不同&#xff0c;是一个虚表,视图只能用来查询。不能做增删改查(虚拟的表) 1.视图的作用 简化查询重写格式化数据频繁访问数据库过滤数据 2.创建视图 -- 创建视图 -- 语法…

计算机三级嵌入式笔记(二)——嵌入式处理器

目录 考点1 嵌入式处理器的结构类型 考点2 嵌入式处理器简介 考点3 ARM处理器概述 考点4 处理器和处理器核 考点5 ARM 处理器的分类 考点6 经典 ARM 处理器 考点7 ARM Cortex 嵌入式处理器 考点8 ARM Cortex实时嵌入式处理器 考点9 ARM Cortex 应用处理器 考点10 AR…

Python群体趋向性潜关联有向无向多图层算法

&#x1f3af;要点 &#x1f3af;算法模型图层节点和边数学定义 | &#x1f3af;算法应用于贝叶斯推理或最大似然优化概率建模的多图层生成模型 | &#x1f3af;算法结合图结构边和节点属性 | &#x1f3af;对比群体关联预测推理生成式期望最大化多图层算法 | &#x1f3af;使…

51单片机嵌入式开发:17、STC89C52的嵌入式 遥控器 控制步进电机 转速 和 转向 操作并 printf打印信息

51单片机嵌入式开发 STC89C52的嵌入式 遥控器 控制步进电机 转速 和 转向 操作并 printf打印信息 51单片机嵌入式开发STC89C52的嵌入式 遥控器 控制步进电机 转速 和 转向 操作并 printf打印信息1 概述2 硬件电路2.1 遥控器2.2 红外接收器电路2.3 STC89C52单片机电路2.4 数码管…

工程视角:数据结构驱动的应用开发--字典(dictionary),列表(list)与实体

这里写目录标题 业务业务场景流程分析 实现数据访问层&#xff08;DAL&#xff09;业务逻辑层&#xff08;BLL&#xff09;用户界面层&#xff08;UI&#xff09;工具类 设计思路为什么抽出工具类关于U层使用字典的好处工程视角 业务 业务场景 在一个金融应用系统中&#xff0c…

【N-MOS】N-mos(场效应管)驱动电路分析

目录 1、MOS管分类 2、MOS管导通原理 3、电路分析 源文件下载 可访问底部联系方式也可前往电子校园网官网搜索关键词 关键词&#xff1a;N-MOS 1、MOS管分类 MOS管是金属(metal)、氧化物(oxide)、半导体(semiconductor)场效应晶体管。FET是场效应管。合在一起是金属氧化物半导…

正则表达式与文本三剑客之grep

目录 前言 一、grep命令 二、基础正则表达式常见元字符 2.1、特殊字符 2.2、定位符 2.3、非打印字符 三、元字符操作实例 3.1、查找特定字符 3.2、利用中括号“[]”来查找集合字符 3.3、查找行首“^”与行尾字符“$” 3.4、查找任意一个字符“.”与重复字符“*” 3.…

ARM系列运行异常排查

一、断点指令BKPT BKPT指令产生软件断点中断,可用于程序的调试。它使处理器停止执行正常指令(使处理器中止预取指)而进入相应的调试程序。 BKPT指令的格式为:BKPT 16位的立即数 二、使用BKPT进行软件异常定位 假设异常发生后,全局变量k变成了88,现在想确认具体是什么…

【题解】UVA1564/SP2883 Widget Factory

题解 题意 题解分析Code 前言 调了好久&#xff0c;还是太菜了 题意 洛谷&#xff1a; SP UVA vjudge SP UVA 题解 分析 转换一下&#xff0c;题目就是让我们解方程组&#xff1a; { ∑ i 1 n a 1 , i x i ≡ b 1 ( m o d 7 ) ∑ i 1 n a 2 , i x i ≡ b 2 ( m o d 7 ) …

Java学习Day13:基础篇3

流程控制 1.if 案例&#xff1a; public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int a sc.nextInt();int b sc.nextInt();int c sc.nextInt();if (ab>c&&ac>b&&bc>a){System.out.println(&quo…

连锁店收银系统源码

系统概况&#xff1a; 专门为零售行业的连锁店量身打造的收银系统&#xff0c;适用于常规超市、生鲜超市、水果店、便利店、零食专卖店、服装店、母婴用品、农贸市场等类型的门店使用。同时线上线下数据打通&#xff0c;线下收银的数据与小程序私域商城中的数据完全同步&#…

06-Ubuntu 22.04LTS安装 Redis | QuickRedis 远程连接

文章目录 1. 安装命令2. 查看redis 状态3. 查看配置文件的位置4. 注释掉bind addr&#xff0c;并设置redis密码5. 设置端口6379开放6. 重启redis6. 本地测试连接7. QuickRedis 远程连接 1. 安装命令 sudo apt-get update sudo apt-get install redis-server2. 查看redis 状态 …

dsa加训

refs: OI Wiki - OI Wiki (oi-wiki.org) 1. 枚举 POJ 2811 熄灯问题 refs : OpenJudge - 2811:熄灯问题 如果要枚举每个灯开或者不开的情况&#xff0c;总计2^30种情况&#xff0c;显然T。 不过我们可以发现&#xff1a;若第i行的某个灯亮了&#xff0c;那么有且仅有第i行和第…

MQ传递用户信息

theme: nico 你们好&#xff0c;我是金金金。 场景 购物车里面有5个商品&#xff0c;用户勾选了并且提交订单了&#xff0c;此时需要删除购物车对应勾选的商品&#xff0c;mq的话涉及到传递用户信息~因为删除对应的购物车商品是需要传递用户信息来知晓对应用户的 生产者 消费者…

SolidWorks 二次开发--创建属性页面及控件事件(二)

在前文中我们学习了如何创建和显示属性页面&#xff0c;本章节将重点介绍如何向属性页面中添加控件。控件是属性页面的基本组成部分&#xff0c;可以是文本框、按钮、复选框等&#xff0c;用于用户交互和数据展示。接下来我们将看到如何定义、配置和操作这些控件&#xff0c;让…