手写简易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实现类的…...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...