手把手实现一个RPC框架

一、RPC前置知识介绍

1.什么是RPC?

RPC是远程过程调用(Remote Procedure Call)的缩写形式。

假设我们有两台服务器A与B,一个在A服务器上部署的应用想要调用B服务器上部署的应用的函数、方法,由于不在同一个内存空间,不能直接调用,因此需要通过网络来表达调用的语义和传达调用的数据。 在Java中,即将被调用的类、方法、参数序列化后通过网络传到目标应用,通过反射的方式调用执行

2.数据交换方式

利用中间件进行数据交换。

直接进行数据交换。

3.现有RPC框架对比

目前主流的RPC框架如下表所示:

4.核心原理

4.1 调用流程

  • Server: Provider,服务提供者
  • Client: Consumer,服务消费者
  • Stub: 存根,服务描述

一次函数调用的流程如下:

  • 首先客户端需要告诉服务端,需要调用的函数,这里函数和进程存在一个映射,客户端远程调用时,需要查一下函数,找到对应的标识,然后执行函数的代码。
  • 客户端需要把本地参数传给远程函数,本地调用的过程中,直接压栈即可,但是在远程调用过程中不在同一个内存里,无法直接传递函数的参数,因此需要客户端把参数转换成字节流,传给服务端,然后服务端将字节流转换成自身能读取的格式,是一个序列化和反序列化的过程。
  • 数据准备好了之后,如何进行传输?网络传输层需要把调用的ID和序列化后的参数传给服务端,然后把计算好的结果序列化传给客户端,因此TCP层即可完成上述过程。

4.2 架构

my-rpc
├── my-rpc-client    -- 客户端
├── my-rpc-codec     -- 序列化与反序列化
├── my-rpc-common    -- 提供一些反射工具
├── my-rpc-protocol  -- 规定数据传输协议
├── my-rpc-server    -- 服务端
├── my-rpc-transport -- 用于client与server的http通信处理
└── my-rpc-example   -- 调用样例

模块依赖关系如下图所示,my-rpc-server和my-rpc-client依赖关系相同。

4.3 各功能模块功能详细介绍

(1)protocal模块

用于规定数据传输协议和规则;

(2)transport模块

  • 该模块主要用于client与server的http通信处理问题,其client请求内容以request类形式封装传输,server响应内容以response类封装返回;
  • 使用jetty容器完成init,start和stop功能;
  • 最重要的是RequestHandler实例的初始化,该抽象类定义于Transport模块,主要用于server处理来自client的请求。其抽象方法实现将在RpcServer类中详细讲解。

(3)common模块

  • common模块主要为一些反射工具,其具体实现如下:
  • getPublicMethods()方法一个用途是Server注册时存储所有的method的ServcieSescriptor。
  • invoke()方法用于执行指定实例对象的method。

(4)codec模块

  • Encoder 编码器
  • Decoder 解码器

(5)server模块

  • 本项目最核心两个模块之一,主要作用是定义了处理client请求的方法。
  • register()方法主要用于注册该class的所有共有方法,并且获取之前讲述的ServiceDescriptor实例与ServiceInstance作为键值对的形式存储。
  • 其内部主要定义了连个变量,一个是需要执行某个method的目标对象,另一个是需要执行的method。
  • 其onRequest()方法通过Servlet的inputStream与OutputStream参数获取来自Client的数据,并且通过获取到的Request实例参数从ServiceManager中get实例对象与method。
  • 因为Request对象中包含有Client获取到的实际参数,因此将上述参数一起传递到ServiceInvoker对象进行执行。

(6)client模块

  • 该模块主要功能有连个一个时动态代理获取实参,一个是请求Server进行过程调用。
  • 其RpcClient类主要是用于处理Client对Server的连接问题,相当于连接池,由有需求时随机返回连接。
  • RpcClient类的getProxy()方法为动态代理,需要重点关注RemoteInvoker类。
  • invoke()方法中对代理方法的参数进行存储封装到Request对象并且最终序列化传递到Server。

(7)example样例

  • 一个加减法的使用样例

4.4 涉及的技术栈

  • 基础:Java、Maven、反射、JDK动态代理
  • 序列化:FastJson
  • 网络:Jetty、URLConnection

二、实现

1.类依赖图

2.实现过程

附加问题:dependencies与dependencyManagement区别是什么

(1)dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承);

(2)dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。

附加问题:Java泛型中Class 、T与Class<?>

单独的T 代表一个类型 ,而 Class代表这个类型所对应的类, Class<?>表示类型不确定的类/

如何创建一个Class类型的实例?

就像使用非泛型代码一样,有两种方式:调用方法 Class.forName() 或者使用类常量X.class。 Class.forName() 被定义为返 回 Class<?>。另一方面,类常量 X.class 被定义为具有类型 Class,所 以 String.class 是Class 类型的。

方法中为什么需要 T修饰呢

泛型的声明,必须在方法的修饰符(public,static,final,abstract等)之后,返回值声明之前。

public static <T> T request2Bean(HttpServletRequest request,Class<T> clazz){}

其中第一个是与传入的参数Class相对应的,相当于返回值的一个泛型,后面的T是返回值类型,代表方法必须返回T类型的(由传入的Class决定)

实现过程:

1.建工程
2.实现通用模块
3.实现序列化模块
4.实现网络模块
5.实现server
6.实现client
7.gk-rpc使用案例
rpc-proto : 基础协议封装Peer:主机+端口ServiceDescriptor:服务描述,将会是注册中心中对应的【服务的key值】包含有【类,方法,返回值类型,参数类型数组】,唯一确定一个方法Request:请求体,持有【服务描述+请求参数数组】Response:默认的响应体封装
rpc_transport: 网络服务封装TransportClient:客户端封装【接口+实现】1.创建连接2.发送数据等待响应:发送inputstream,等待outputstream3.关闭连接TransportServer:服务端封装【接口+实现】1.启动监听: servlet管理2.接收请求:接收请求,反序列化获取对象,处理调用,返回数据3.关闭监听
rpc-common: 工具类封装ReflectionUtils:根据class创建对象根据class获取该类所有公共方法invoke方法调用 : public static Object invoke(Object object, Method method, Object... args)
rpc-codec: 序列化封装【接口+实现】Encoder 转二进制Decoder 转对象
rpc-server: 服务端封装RpcServerConfig:服务配置类HTTPTransportServer:默认服务实例类JSONEncoder:序列化实例类JSONDecoder:反序列化实例类port:监听端口ServiceInstance:服务的实例 --> 哪个对象暴露出哪个方法target:对象method:方法ServiceManager:管理rpc的所有服务Map<ServiceDescriptor, ServiceInstance> services:要将服务描述,服务实例作为key-value存储,便于客户端传来时,能够找到准确地实例,调用正确的方法register:服务注册【register(Class<T> interfaceClass, T bean) 】接口类 + 对象bean:将对象中的每一个方法都当做一个ServiceInstance注册进map中lookup:服务查找【lookUp(Request request)】获取请求中的ServiceDescriptor,去map中取出ServiceInvoke:【服务的调用】invoke(ServiceInstance serviceInstance, Request request):通过request的ServiceDescriptor找到服务的实例通过反射调用方法,传入参数ReflectionUtils.invoke(serviceInstance.getTarget(), serviceInstance.getMethod(),request.getParameters())RPCServer:【服务的封装】1.设置RpcServerConfig config2.反射获取网络实例     ReflectionUtils.newInstance(config.getTransportClass());3.反射获取序列化实例   ReflectionUtils.newInstance(config.getEncoderClass());4.反射获取反序列化实例 ReflectionUtils.newInstance(config.getDecoderClass());5.创建服务调用对象: this.serviceInvoke = new ServiceInvoke();6.创建服务管理对象:this.serviceManager = new ServiceManager();7.初始化网络实例:this.net.init(config.getPort(), this.handler); 此时只是准备好服务信息,并未开启监听8.register:服务注册 register(Class<T> interfaceClass, T bean) {serviceManager.register(interfaceClass, bean); }9.start:this.net.start 开启10.stop:this.net.stop  关闭11.handler请求处理:1.接收inputStream2.反序列化获得Request3.根据ServiceManager.lookup(request)找到实例ServiceInstance4.通过Object invoke = serviceInvoke.invoke(sis, request);得到响应结果并封装到 response.setData(invoke);5.序列化Response6.write
rpc-client: 客户端封装

结果展示图:


【RPC学习之旅】手把手实现一个RPC框架相关推荐

  1. 【编程实践】为了带你搞懂RPC,我们手写了一个RPC框架

    如今分布式系统大行其道的年代,RPC 有着举足轻重的地位.风靡的 Duboo.Thrift.gRpc 等框架各领风骚,深入了解RPC是新手也是老鸟的必修课.你知道 RPC 的实现原理吗?想动手实现一个 ...

  2. 虚拟盘rpc服务器不可用,rpc服务器不可用,手把手教你rpc服务器不可用怎么办

    有网友表示进入磁盘管理对磁盘进行分区.更改盘符或压缩卷等操作的时候出现"RPC服务器不可用"的报错,通常我们在安装打印机或者虚拟磁盘时,将出现此提示.下面,小编给大家介绍rpc服务 ...

  3. WCF学习之旅----正式篇之基础框架

    2019独角兽企业重金招聘Python工程师标准>>> 服务类包括服务契约IWCFService.操作契约OperationContract.和数据契约DataContract. u ...

  4. 《Unity Shader入门精要》学习笔记第5章 开始Unity Shader学习之旅

    本文章用于帮助自己学习,因此只记录一些个人认为比较重要或者还不够熟悉的内容. 原作者:http://blog.csdn.net/candycat1992/article/ 第五章 开始Unity Sh ...

  5. 耗时两周手撸了一个 RPC 轮子,是驴子是马拉出来遛遛

    手撸 RPC 轮子系列文章目录: 「从零开始造 RPC 轮子系列」01 我为什么要去造一个轮子? 「从零开始造 RPC 轮子系列」02 演示轮子,是驴是马拉出来遛遛] (TODO)「从零开始造 RPC ...

  6. 如何设计一个 RPC 系统

    本文由云+社区发表 RPC是一种方便的网络通信编程模型,由于和编程语言的高度结合,大大减少了处理网络数据的复杂度,让代码可读性也有可观的提高.但是RPC本身的构成却比较复杂,由于受到编程语言.网络模型 ...

  7. 从零开始写一个RPC框架的详细步骤

    http://blog.csdn.net/liu88010988/article/details/51547592 定位 所谓定位就是回答几个问题,我出于什么目的要写一个框架,我的这个框架是干什么的, ...

  8. 【Rust日报】 2019-08-01:brpc-rs - X-lab 实验室新推出的一个rpc库

    paperclip - OpenAPI 规范的 Rust 实现 在完成后,它会实现: Rust 中的服务器端.客户端和命令行端的高效的.编译时检查的.类型安全的 HTTP API 的生成: 支持处理. ...

  9. 手写一个 RPC 远程调用(C++)

    手写一个 RPC 远程调用(C++) 版权声明 个人作品,禁止转载 参考资料 Build Remote Procedure Calls (RPC) - from scratch in C(https: ...

最新文章

  1. 根据 UserAgent 判断网页是在浏览器、或在微信、或在APP中
  2. 在win10下面新建一个虚拟网卡
  3. J2EE dynamic web工程搭建 struts2
  4. android 居右属性,使用layoutDirection属性设置布局靠左或靠右
  5. python如何在所有线程结束后执行最后操作_Python线程的生命周期
  6. 基于流程管理,提高工作质量和效率
  7. Golang基本变量
  8. 再谈 Linux下的nanosleep函数
  9. python去除行末符EOL的一般方法
  10. java线程集合点_Jmeter集合点(线程组和同步定时器)
  11. 迁移学习SSD深度网络模型,实现文本行检测
  12. python mro c3_Python的MRO以及C3线性化算法
  13. JS中的showModelDialog详解和实例
  14. 卡巴斯基重新激活试用版的方法
  15. USB-HID鼠标键盘驱动
  16. lbochs模拟器最新版_Bochs模拟器下载_Bochs模拟器免费[系统增强]-下载之家
  17. 等差数列_等比数列公式
  18. 前后端分离整合阿里云OSS图片上传功能
  19. Cobbler自动安装windows10
  20. L2-039 清点代码库 - java

热门文章

  1. Java线程上下文切换
  2. WAL的概念与实践分析
  3. 【Kivy自学笔记】Python开发App必备!Kivy基础控件详解(含视频源码)
  4. Android网络请求加密机制详解
  5. 关于小程序中地图的应用(联动搜索,模糊查询,定位导航)
  6. 医疗管理系统-手机验证码登录
  7. Android中如何优雅的定义常量
  8. office爆破软件
  9. 联邦学习之安全聚合SMPC
  10. HTML——js设置计时器和清除计时器的方法