手写简易RPC框架
目录
简介
服务提供者
服务注册:注册中心
HttpServerHandler处理远程调用请求
consumer服务消费端
简介
RPC(Remote Procedure Call)——远程过程调用,它是一种通过网络从远程计算机程序上请求服务, 而不需要了解底层网络技术的协议,在面向对象的编程语言中,远程过程调用即是远程方法调用
基本实现思路如下:

项目结构:
- provider服务提供
- consumer服务消费
- registry注册
-
protocol协议
服务提供者
- 定义服务接口
接口HelloService
public interface HelloService {String sayHello(String message);
}
- 实现类HelloServiceImpl
public class HelloServiceImpl implements HelloService {@Overridepublic 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;@Overridepublic 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;}
@Overridepublic 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) {
//创建URLURL 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){// 实例一个tomcatTomcat tomcat = new Tomcat();
// 构建serverServer server = tomcat.getServer();
// 获取serviceService service = server.findService("Tomcat");
// 构建ConnectorConnector connector = new Connector();connector.setPort(port);connector.setURIEncoding("UTF-8");
// 构建EngineEngine engine = new StandardEngine();engine.setDefaultHost(hostname);
// 构建HostHost host = new StandardHost();host.setName(hostname);
// 构建ContextString 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();// 启动tomcattomcat.getServer().await();// 接受请求}catch (LifecycleException e){e.printStackTrace();}}
}
- DispatcherServlet
public class DispatcherServlet extends HttpServlet {
@Overrideprotected 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) {
//创建URLURL 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) {
//封装一个invocationInvocation 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);}
}
相关文章:
手写简易RPC框架
目录 简介 服务提供者 服务注册:注册中心 HttpServerHandler处理远程调用请求 consumer服务消费端 简介 RPC(Remote Procedure Call)——远程过程调用,它是一种通过网络从远程计算机程序上请求服务, 而不需要了解…...
基于孪生网络的目标跟踪
一、目标跟踪 目标跟踪是计算机视觉领域研究的一个热点问题,其利用视频或图像序列的上下文信息,对目标的外观和运动信息进行建模,从而对目标运动状态进行预测并标定目标的位置。具体而言,视觉目标(单目标)…...
苏州狮山广场能耗管理系统
摘要:随着社会生活水平的提高,经济的繁荣发展,人们对能源的需求逐渐增长,由此带来的能源危机日益严重。商场如何实时的了解、分析和控制商场的能源消耗已成为需要解决的迫在眉睫的难题。传统的能源消耗智能以月/季度/年为周期进行…...
Jupyter Notebook 10个提升体验的高级技巧
Jupyter 笔记本是数据科学家和分析师用于交互式计算、数据可视化和协作的工具。Jupyter 笔记本的基本功能大家都已经很熟悉了,但还有一些鲜为人知的技巧可以大大提高生产力和效率。在这篇文章中,我将介绍10个可以提升体验的高级技巧。 改变注释的颜色 颜…...
CF 751 --B. Divine Array
Black is gifted with a Divine array a consisting of n (1≤n≤2000) integers. Each position in a has an initial value. After shouting a curse over the array, it becomes angry and starts an unstoppable transformation. The transformation consists of infinite…...
Springcloud1--->Eureka注册中心
目录 Eureka原理Eureka入门案例编写EurekaServer将user-service注册到Eureka消费者从Eureka获取服务 Eureka详解基础架构高可用的Eureka Server失效剔除和自我保护 Eureka原理 Eureka:就是服务注册中心(可以是一个集群),对外暴露自…...
面试阿里、字节全都一面挂,被面试官说我的水平还不如应届生
测试员可以先在大厂镀金,以后去中小厂毫无压力,基本不会被卡,事实果真如此吗?但是在我身上却是给了我很大一巴掌... 所谓大厂镀金只是不卡简历而已,如果面试答得稀烂,人家根本不会要你。况且要不是大厂出来…...
JAVA开发(记一次删除完全相同pgSQL数据库记录只保留一条)
进行数据管理时,无效数据可能会对生产力和决策质量造成严重的影响。如何发现和处理无效数据变得愈发重要。一起来唠唠你会如何处理无效数据吧~ 方向一:介绍无效数据的概念 最近遇到了pg数据库表中的大量数据重复了,需要删除其中的一条。一条…...
音视频八股文(7)-- 音频aac adts三层结构
AAC介绍 AAC(Advanced Audio Coding)是一种现代的音频编码技术,用于数字音频的传输和存储领域。AAC是MPEG-2和MPEG-4标准中的一部分,可提供更高质量的音频数据,并且相比于MP3等旧有音频格式,AAC需要更少的…...
Docker代码环境打包进阶 - DockerHub分享镜像
1. Docker Hub介绍 Docker Hub是一个广泛使用的容器镜像注册中心,为开发人员提供了方便的平台来存储、共享和分发Docker容器镜像。它支持版本控制、访问控制和自动化构建,并提供了丰富的公共镜像库,方便开发人员快速获取和使用各种开源应用和…...
SQL进阶-having子句的力量
SQL进阶-having子句的力量 having子句是理解SQL面向集合这一本质的关键。 在以前的SQL标准里面,having子句必须和group by子句一起使用,但是按照现在的SQL标准,having子句是可以单独使用的 可以与case 表达式或者自连接等结合使用。表不是文件…...
Electron 如何创建模态窗口?
目录 前言一、模态窗口1.Web页面模态框2.Electron中的模态窗口3.区分父子窗口与模态窗口 二、实际案例使用总结 前言 模态框是一种常用的交互元素,无论是在 Web 网站、桌面应用还是移动 APP 中,都有其应用场景。模态框指的是一种弹出窗口,它…...
诺贝尔化学奖:酶分子“定向进化”
2018年,诺贝尔化学奖迎来了历史上第五位女性得主——加州理工学院的Frances H. Arnold教授,以表彰她在“酶的定向进化”这一领域的贡献。 1、“酶的定向进化”到底是什么? 这里有三个点,“酶”、“进化”还有“定向”:…...
Centos8下源码编译安装运行Primihub
参考文献 PrimiHub 本地编译启动How to install Bazel on CentOS 8 Linux or Redhat 8/7 编译启动步骤 由于历史原因,服务器是Centos8操作系统,所以源码编译异常的麻烦。特此记录如下。 采用源码编译方式可以在一步步的运行过程中对整个流程进行深刻…...
嘉兴桐乡考证培训-23年教资认定注意事项你知道吗?
又到了新的一年了,去年错过认定的同学们可以竖起耳朵啦~ 每年认定机会有两次,大部分省份一般上半年下半年各一次。 问:在校生可以认定么? 答:可以,但有年级限制:本科生大四最后一学期…...
oracle客户端的安装教程
文章目录 一、安装前的准备工作 1.1、百度网盘安装包的连接 1.2、百度网盘oracle11g软件包 二、oracle数据库客户端的安装与数据的准备 安装步骤 前言 本文主要讲解oracle客户端的安装与简单使用过程 一、安装前的准备工作 1.1、百度网盘安装包的连接 客户端的软件包 …...
python 文件操作 , 异常处理 , 模块和包
文件操作 1.写数据 # open(name, mode) # name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)。 # mode:设置打开文件的模式(访问模式):只读、写入、追加等。 #1.打开文件---通道建立--申请资源 # w 模式会清空之前的内…...
AIGC技术研究与应用 ---- 下一代人工智能:新范式!新生产力!(1-简介)
文章大纲 AI GC简介决策式/分析式AI(Discriminant/Analytical AI)和生成式AI (Generative AI)参考文献与学习路径模型进化券商研报陆奇演讲AI GC 《我,机器人》中所演绎的一样,主角曾与机器人展开了激烈的辩论,面对“机器人能写出交响乐吗?”“机器人能把画布变成美丽…...
Flask restful分页接口实现
1.先定义一个工作信息表: 指定一些相关的字段:工作名称、年限、级别等 class Work(db.Model):__tablename__ = workid = db.Column(db.Integer, primary_key=True)workName = db.Column(db.String(5),nullable=False)year = db.Column(db.String(20), nullable=False)level = …...
27事务管理AOP
一、MySQL事务回顾 二、Spring事务管理 Spring框架的第一大核心:IOC控制反转 在DeptServiceImpl下删除部门方法下新加一个删除员工信息的操作,注意:此时的id是部门id。 1、问题分析 2、Transactional-Spring事务管理 一般是在Service实现类的…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
