从零开始手写RPC框架(1)

学习java后端也有一段时间了,在网上寻一些教程和github上的开源库,学习从零开始手写一个RPC,学习各位大牛的代码适当修改,并贴上自己的一些见解和注释。

目录

  • RPC简介
    • RPC和HttpClient的区别和共同点
    • 常见RPC框架
  • RPC框架设计
  • 常见序列化协议

RPC简介

RPC (Remote Procedure Call)是一种技术,允许程序调用在不同地址空间(通常是在另一台计算机上)的过程或函数,就像调用本地过程一样,隐藏了网络通信的细节。RPC 框架通常负责打包调用的参数,通过网络发送给服务提供者,然后将结果返回给调用者。

RPC由六个部分实现:

1. 客户端(服务消费端) :调用远程方法的一端。
2. 客户端 Stub(桩) : 这其实就是一代理类。代理类主要做的事情很简单,就是把你调用方法、类、方法参数等信息传递到服务端。
其负责将方法、参数等组装成能够进行网络传输的消息体(序列化):RpcRequest;找到远程服务的地址,并将消息发送到服务提供端;接收到消息并将消息反序列化为 Java 对象:RpcResponse ,这样也就得到了最终结果。
3. 网络传输 : 网络传输就是你要把你调用的方法的信息比如说参数啊这些东西传输到服务端,然后服务端执行完之后再把返回结果通过网络传输给你传输回来。网络传输的实现方式有很多种比如最基本的 Socket 或者性能以及封装更加优秀的 Netty(推荐)。
4. 服务端 Stub(桩) :这个桩就不是代理类了。理解为桩实际不太好。这里的服务端 Stub 实际指的就是接收到客户端执行方法的请求后,去指定对应的方法然后返回结果给客户端的类。
其收到消息将消息反序列化为 Java 对象: RpcRequest;根据RpcRequest中的类、方法、方法参数等信息调用本地的方法;得到方法执行结果并将组装成能够进行网络传输的消息体:RpcResponse(序
列化)发送至消费方;
5. 服务端(服务提供端) :提供远程方法的一端。

RPC和HttpClient的区别和共同点

之前学习过HttpClient

它们都是在网络编程中常见的技术,用于在不同的计算机之间进行数据交换和通信。它们各自有不同的应用场景和特点,但也存在一些共同点。

RPC特点:

透明性: 对于开发者而言,远程调用和本地调用非常相似,隐藏了网络请求的复杂性。
多协议支持: 支持多种传输协议,如 HTTP、TCP、UDP 等。
高效: 通常使用专门的二进制协议,效率比 HTTP 更高。
强类型: 调用通常基于接口定义,具有强类型校验。

RPC应用:

微服务架构中服务间通信
分布式系统中不同组件间的交互

HttpClient 是一个用于发送 HTTP 请求和接收 HTTP 响应的类库或接口,存在于多种编程语言和框架中。它是 Web 开发中常用的工具,用于执行 HTTP 操作(如 GET、POST、PUT、DELETE 等)。

HttpClient 特点:

灵活性: 支持广泛的 HTTP 功能,包括各种方法、头部、身份验证等。
文本基础: 基于文本的请求和响应(如 JSON、XML),易于阅读和调试。
广泛支持: 几乎所有的 Web 开发语言和框架都支持 HTTP 客户端库。
状态无关: HTTP 是无状态的,每个请求都是独立的。

HttpClient 应用:

Web API 调用
微服务架构中的服务间通信
任何需要 Web 资源交互的场景

尽管 RPC 和 HttpClient 在设计和使用场景上有所不同,但它们也有一些共同点:

网络通信: 两者都用于在网络中的不同计算机之间进行数据交换。
服务消费: 无论是通过 RPC 还是 HttpClient,都可以作为服务消费者,调用远程服务或 API。
抽象层: 它们提供了与远程系统通信的抽象层,封装了底层的网络细节。

总的来说,选择 RPC 还是 HttpClient 取决于具体的应用场景和需求。RPC 更适合于对性能有高要求和需要强类型支持的内部服务通信,而 HttpClient 更适合于 Web API 调用和需要灵活性和广泛兼容性的场景。



常见RPC框架

RPC 框架指的是可以让客户端直接调用服务端方法,就像调用本地方法一样简单的框架

Apache Dubbo

一款微服务框架,为大规模微服务实践提供高性能 RPC 通信、流量治理、可观测性等解决方案, 涵盖 Java、Golang 等多种语言 SDK 实现。提供了从服务定义、服务发现、服务通信到流量管控等几乎所有的服务治理能力,支持 Triple 协议(基于 HTTP/2 之上定义的下一代 RPC 通信协议)、应用级服务发现、Dubbo Mesh (Dubbo3 赋予了很多云原生友好的新特性)等特性。
由阿里开源,后来加入了 Apache 。正是由于 Dubbo 的出现,才使得越来越多的公司开始使用以及接受分布式架构。是比较优秀的国产开源项目。

Dubbo 不论是从功能完善程度、生态系统还是社区活跃度来说都是最优秀的

github

官网

Motan

新浪微博开源的一款 RPC 框架,网上的资料比较少,更像是一个精简版的 Dubbo,设计更加精简,功能更加纯粹。公司实际使用的话,还是推荐 Dubbo 。

Motan 中文文档

从 Motan 看 RPC 框架设计

Dubbo 和 Motan 主要是给 Java 语言使用。如果需要跨多种语言调用的话,可以考虑使用 gRPC。

gRPC

Google 开源的一个高性能、通用的开源 RPC 框架。其由主要面向移动应用开发并基于 HTTP/2协议标准而设计(支持双向流、消息头压缩等功能,更加节省带宽),基于 ProtoBuf 序列化协议开发,并且支持众多开发语言。gRPC 的设计导致其几乎没有服务治理能力。如果你想要解决这个问题的话,就需要依赖其他组件比如腾讯的 PolarisMesh(北极星)了。

Github

官网

Thrift

Facebook 开源的跨语言的 RPC 通信框架,目前已经捐献给 Apache 基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用。相比于 gRPC 支持的语言更多。
gRPC 和 Thrift 虽然支持跨语言的 RPC 调用,但是它们只提供了最基本的 RPC 框架功能,缺乏一系列配套的服务化组件和服务治理功能的支撑。

官网




RPC框架设计

参考RPC框架Dubbo的架构:

在这里插入图片描述

可以看到大体分为这几个部分服务提供方与运行服务的容器、服务注册与发现中心、服务消费方、监控中心。

注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。我们不实现监控模块,有兴趣可以研究一下Dubbo 的监控模块的设计。

实现一个最基本的 RPC 框架需要哪些东西:

注册中心

注册中心负责服务地址的注册与查找,相当于目录服务。服务端启动的时候将服务名称及其对应的地址(ip+port)注册到注册中心,服务消费端根据服务名称找到对应的服务地址。有了服务地址之后,服务消费端就可以通过网络请求服务端了。


比较推荐使用 Zookeeper 作为注册中心。也可以使用 Nacos ,甚至是 Redis。ZooKeeper 为我们提供了高可用、高性能、稳定的分布式数据一致性解决方案,通常被用于实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。并且,ZooKeeper 将数据保存在内存中,性能是非常棒的。在“读”多于“写”的应用程序中尤其地高性能,因为“写”会导致所有的服务器间同步状态。(“读”多于“写”是协调服务的典型场景)。

网络传输

要调用远程的方法,就要发送网络请求来传递目标类和方法的信息以及方法的参数等数据到服务提供端。
网络传输具体推荐使用基于 NIO 的网络编程框架 Netty ,是最好的选择。Socket是Java 中最原始、最基础的网络通信方式,Socket 阻塞IO、性能低并且功能单一。使用同步非阻塞的 I/O 模型 NIO ,但是用它来进行网络编程太麻烦。
Netty 是一个基于 NIO 的 client-server(客户端服务器)框架,使用它可以快速简单地开发网络应用程序。它极大地简化并简化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面甚至都要更好。支持多种协议如 FTP,SMTP,HTTP 以及各种二进制和基于文本的传统协议。

序列化和反序列化

我们的 Java 对象没办法直接在网络中传输。为了能够让Java 对象在网络中传输我们需要将其序列化为二进制的数据。不仅网络传输的时候需要用到序列化和反序列化,将对象存储到文件、数据库等场景都需要用到序列化和反序列化。


JDK 自带的序列化,只需实现java.io.Serializable 接口即可,不过这种方式不支持跨语言调用并且性能比较差。现在比较常用序列化的有 hessian、kryo、protostuff、Protobuf等

动态代理

RPC 的主要目的就是让我们调用远程方法像调用本地方法一样简单,我们不需要关心远程方法调用的细节比如网络传输。动态代理就可以屏蔽远程方法调用的底层细节。

负载均衡

负载均衡就是为了避免单个服务器响应同一请求,容易造成服务器宕机、崩溃等问题。

传输协议

我们还需要设计一个私有的 RPC 协议,这个协议是客户端(服务消费方)和服务端(服务提供方)交流的基础。定义需要传输哪些类型的数据, 并规定每一种类型的数据应该占多少字节。这样在接收到二进制数据之后,就可以正确的解析出我们需要的数据,这有一点像密文传输。


一些标准的 RPC 协议包含下面这些内容:

魔数 : 通常是 4 个字节。这个魔数主要是为了筛选来到服务端的数据包,有了这个魔数之后,服务端首先取出前面四个字节进行比对,能够在第一时间识别出这个数据包并非是遵循自定义协议的,也就是无效数据包,为了安全考虑可以直接关闭连接以节省资源。
序列化器编号 :标识序列化的方式,比如是使用 Java 自带的序列化,还是 json,kryo 等序列化方式。
消息体长度 : 运行时计算出来。

基于此,我们实现一款基于 Netty+kryo+Zookeeper的RPC 框架。




常见序列化协议

JDK 自带的序列化方式一般不会用 ,因为序列化效率低并且存在安全问题,而且不支持跨语言调用。JSON 和 XML 这种属于文本类序列化方式。虽然可读性比较好,但是性能较差。

JDK 自带的序列化方式

只需实现 java.io.Serializable接口即可:

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@ToString
public class RpcRequest implements Serializable {
	private static final long serialVersionUID = 1905122041950251207L;
	private String requestId;
	private String interfaceName;
	private String methodName;
	private Object[] parameters;
	private Class<?>[] paramTypes;
	private RpcMessageTypeEnum rpcMessageTypeEnum;
}
serialVersionUID 作用
序列化号 serialVersionUID 属于版本控制的作用。反序列化时,会检查serialVersionUID 是否和当前类的 serialVersionUID 一致。如果 serialVersionUID 不一致则会抛出InvalidClassException 异常。强烈推荐每个序列化类都手动指定其serialVersionUID ,如果不手动指定,那么编译器会动态生成默认的 serialVersionUID 。

serialVersionUID 不是被 static 变量修饰了吗?为什么还会被“序列化”?
static 修饰的变量是静态变量,位于方法区,本身是不会被序列化的。 static 变量是属于类的而不是对象。你反序列之后, static 变量的值就像是默认赋予给了对象一样,看着就像是static 变量被序列化,实际只是假象罢了。

如果有些字段不想进行序列化怎么办?
使用transient关键字修饰。transient 只能修饰变量,不能修饰类和方法。transient 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int 类型,那么反序列后结果就是0 。static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。

Kryo

Kryo 是一个高性能的序列化/反序列化工具,由于其变长存储特性并使用了字节码生成机制,拥有较高的运行速度和较小的字节码体积。另外,Kryo 已经是一种非常成熟的序列化实现了,已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛的使用。

Kryo专门针对 Java 语言序列化方式并且性能非常好,像 Protobuf、 ProtoStuff、hessian 这类都是跨语言的序列化方式,如果有跨语言需求的话可以考虑使用。

Github

Protobuf

Protobuf 出自于 Google,性能还比较优秀,也支持多种语言,同时还是跨平台的。就是在使用中过于繁琐,因为你需要自己定义 IDL 文件和生成对应的序列化代码。这样虽然不灵活,但是,另一方面导致protobuf 没有序列化漏洞的风险。

Github

ProtoStuff

protostuff 基于 Google protobuf,但是提供了更多的功能和更简易的用法。虽然更加易用,但是不代表 ProtoStuff 性能更差。

Github

Hessian

Hessian 是一个轻量级的,自定义描述的二进制 RPC 协议。Hessian 是一个比较老的序列化实现了,并且同样也是跨语言的。

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

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

相关文章

线性规划的标准型转换

对于任意给定的线性规划的问题,其实其本身可能是不符合线性规划标准型的需求的,但是如果通过一系列的等价变化的话,是可以将该问题转换为标准型的线性规划问题,例如如下的线性规划问题: 添加图片注释,不超过 140 字(可选) 给定的线性规划问题存在若干方面不满足最小值,…

就业班 2401--2.26 Linux Day5--进程管理一

一、权限扩展 文件权限管理之&#xff1a; 隐藏权限防止root误删除 文件属性添加与查看 [rootlinux-server ~]# touch file1 file2 file3 1.查看文件属性 [rootlinux-server ~]# lsattr file1 file2 file3 ---------------- file1 ---------------- file2 ----------------…

Vivado时序报告之Check_timing详解

目录 一、前言 二、Check_Timing 2.1 含义解释 2.2 工程代码 2.3 时序约束 2.4 Check_timing报告 no clock pulse_width_clock unconstrained_internal_endpoints no_input_delay no_output_delay multiple_clock generated_clocks loops partial_input_delay …

在 Python 中确定一个数字是否等于 0,考虑精度问题

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 在 Python 中&#xff0c;特别是在处理浮点数时&#xff0c;确定一个数字是否等于 0 时&#xff0c;必须考虑精度问题。由于计算机使用二进制表示数字&#xff0c;浮点运算可能会引入微小的误差。这意味…

2024年抖店创业项目分享,新手不能错过的教程分享,快收藏!

大家好&#xff0c;我是电商花花。 在我看来&#xff0c;2024年最受欢迎的电商创业项目还得是抖音小店无货源&#xff0c;我依然看好今年抖音电商的市场&#xff0c;并且对于抖音小店我们会做&#xff0c;会一直做下去。 抖音一直都属于兴趣电商&#xff0c;会根据你的兴趣去…

.Encryptedd@mailfence.com.faust勒索病毒数据怎么处理|数据解密恢复

导言&#xff1a; 在数字时代的今天&#xff0c;我们享受着科技带来的便利和创新&#xff0c;但也要时刻警惕着隐患。勒索软件就是其中之一&#xff0c;它们以一种毒害般的形式悄然而至&#xff0c;将我们的数字世界变得一片混乱。今天&#xff0c;我们要聚焦的是 .Encrypted…

ModStartCMS v8.1.0 图片前端压缩,抖音授权登录

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议&#xff0c;免费且不限制商业使用。 功能特性 丰富的模块市…

价格战打响!阿里云服务器和腾讯云服务器价格对比

2024年阿里云服务器和腾讯云服务器价格战已经打响&#xff0c;阿里云服务器优惠61元一年起&#xff0c;腾讯云服务器62元一年&#xff0c;2核2G3M、2核4G、4核8G、8核16G、16核32G、16核64G等配置价格对比&#xff0c;阿腾云atengyun.com整理阿里云和腾讯云服务器详细配置价格表…

2.29总结

P1802 5 倍经验日 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目背景 现在乐斗有活动了&#xff01;每打一个人可以获得 5 倍经验&#xff01;absi2011 却无奈的看着那一些比他等级高的好友&#xff0c;想着能否把他们干掉。干掉能拿不少经验的。 题目描述 现在 absi…

windows 中, bash: conda: command not found(已解决)

git bash 中运行conda命令&#xff0c;出现这种错误&#xff0c;原因是你没有在git bash中 配置conda&#xff0c;导致git bash无法找到conda 那就配置一下&#xff0c;找到你的conda的安装位置下的bash.sh文件&#xff0c;一般在安装位置&#xff08;我的安装在C盘的自定义路径…

『Linux从入门到精通』第 ㉑ 期 - 文件系统详解

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f427;认识磁盘&#x1f427;逻辑抽象&#x1f427;文件系统&#x1f426;Block&#x1f426;Block Group&#x1f414;Block Group 的组成部分 &#x1f426;Superblock(超级区块)&#x1f426;Group Description(…

typescript 的数据类型有哪些

&#x1f469; 个人主页&#xff1a;不爱吃糖的程序媛 &#x1f64b;‍♂️ 作者简介&#xff1a;前端领域新星创作者、CSDN内容合伙人&#xff0c;专注于前端领域技术&#xff0c;成长的路上共同学习共同进步&#xff0c;一起加油呀&#xff01; ✨系列专栏&#xff1a;前端面…

吸猫毛空气净化器哪个好?推荐除猫毛好的宠物空气净化器品牌

如今&#xff0c;越来越多的家庭选择养宠物&#xff01;虽然家里变得更加温馨&#xff0c;但养宠可能会带来异味和空气中的毛发增多可能会引发健康问题&#xff0c;这也是一个大问题。 但我不想家里到处都是异味&#xff0c;尤其是便便的味道&#xff0c;所以很需要一款能够处…

Unity(第八部)Vector3的三维向量和旋转(坐标和缩放也简单讲了一下)

对了&#xff0c;Unity的生命周期自行百度吧&#xff1b;我这边整理的都不是很满意 Vector 是结构体 Vector2是指里面有两个变量 Vector3是指里面有三个变量 Vector4是指里面有四个变量 Vector3常用的变量就是x y z,所以&#xff0c;它可以代表坐标、旋转、缩放、三维向量 创…

C语言数据结构基础—单链表相关数据结构题目6道

上一篇博客中&#xff0c;我们大致的讲解了单链表相关的各种接口。接下来我们通过例题来运用类似的思想&#xff0c;达到学以致用的目的。 1.移除链表元素 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 没有说明头结点是什么&#xff0c;默认就是第一个元素&am…

智能汽车软硬件产品CES展示汽车技术新亮点

智能汽车是汽车产业发展的新趋势&#xff0c;是未来汽车发展的必然方向。智能汽车是指搭载了先进的传感器、控制器、执行器等部件&#xff0c;并融合了人工智能、自动驾驶等技术&#xff0c;能够实现部分或完全自动驾驶、智能网联等功能的汽车。 近年来&#xff0c;智能汽车技…

ensp模拟单臂路由实现不同两个网段主机访问

拓扑结构图如下 1.pc机配置略过 2.交换机配置 三个接口&#xff0c;两个连接pc&#xff0c;连接方式access&#xff0c;一个连接路由器 连接方式trunk sy #进入系统 视图模式 undo info-center enable #关闭信息 vlan batch 10 20#批量创建vlan int g 0/0/2#进入2端口 p…

Java图书管理系统---命令行

项目列表 Book包 Book类内包含book的基本属性 BookList类初始化图书列表并且提供图书的属性方法 User包 Administrator类 common类 operator包 功能接口 新增图书功能 借阅图书功能 删除图书功能 显示图书功能 查找图书功能 归还图书功能 结束释放资源功能 运行…

MySQL进阶:大厂高频面试——各类SQL语句性能调优经验

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;MySQL进阶&#xff1a;强推&#xff0c;冲大厂必精通&#xff01;MySQL索引底层&#xff08;BTree&#xff09;、性能分析、使用…

批量获取图片(下)

1.问题分析 我们观察下之前获得的图片&#xff0c;可以发现图片的清晰度不够。 放大之后比较模糊&#xff0c;这样的图叫做缩略图&#xff0c;那该如何获得高清海报图片呢&#xff1f; 为了找到高清图片&#xff0c;我们需要对网页结构进行分析&#xff0c;找到高清图对应的链…