深入浅出 RPC框架

news2025/1/19 8:04:14

RPC 框架分层设计

01 基本概念

1.1 本地函数调用

以上步骤只是为了说明原理。事实上编译器经常会做优化,对于参数和返回值少的情况会直接将其存放在寄存器,而不需要压栈弹栈的过程,甚至都不需要调用call,而直接做inline操作

1.2 远程函数调用(RPC—Remote Procedure Calls )

1、函数映射

我们怎么告诉支付服务我们要调用付款这个函数,而不是退款或者充值呢?

在本地调用中,函数体是直接通过函数指针来指定的,我们调用哪个方法,编译器就自动帮我们调用它相应的函数指针

但是在远程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。

所以函数都有自己的一个ID,在做 RPC的时候要附上这个 ID,还得有个 ID 和函数的对照关系表,通过 ID找到对应的函数并执行。

2、客户端怎么把参数值传给远程的函数呢?

在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。

但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。

这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式

3、 远程调用往往用在网络上,如何保证在网络上高效稳定地传输数据?

1.3 RPC 概念模型

1.4 一次 RPC 完整过程

相比本地函数调用,远程调用的话我们不知道对方有哪些方法,以及参数长什么样,

所以需要有一种方式来 描述 或者 声明 我有哪些方法,方法的参数都是什么样子的,

这样的话大家就能按照这个来调用,这个描述文件就是 IDL 文件。

刚才我们提到服务双方是通过约定的规范进行远程调用,双方都依赖同一份IDL文件,需要通过工具来生成对应的生成文件,具体调用的时候用户代码需要 依赖 生成代码,所以可以把用户代码和生成代码看做一个整体

编码只是解决了跨语言的数据交换格式,但是如何通讯呢?需要制定通讯协议,

以及数据如何传输?我的网络模型如何呢?那就是这里的 transfer 要做的事情。

1.5 RPC好处

单一职责,开发(采用不同的语言)、部署以及运维(上线独立)都是独立

可扩展性强,例如压力过大的时候可以独立扩充资源,

底层基础服务可以复用,节省资源 某个模块发生故障,不会影响整体的可靠性

1.6RPC带来的问题

1.7 总结

1.本地函数调用和RPC 调用的区别:

RPC需要解决:函数映射、数据转成字节流、网络传输

2.RPC 的概念模型:User、User-Stub、RPC-Runtime、Server-Stub、Server

3. 一次 PRC 的完整过程,并讲解了RPC的基本概念定义

4.RPC 带来好处的同时也带来了不少新的问题,将由 RPC 框架来解决

02分层设计

2.1 分层设计— 以Apache Thrift为例

Code 是用户自己编写的逻辑代码 不在框架范畴;

2.2 编解码层

生成代码也是编解码层的一部分

2.3 编解码层—生成代码

2.4 编解码层—数据格式

语言特定编码格式:

这种编码形式 好处 是非常方便,可以用很少的额外代码实现内存对象的保存与恢复,这类编码通常与特定的编程语言深度绑定,其他语言很难读取这种数据。

如果以这类编码存储或传输数据,那你就和这门语言绑死在一起了。安全和兼容性也是问题

文本格式:

文本格式具有人类可读性,数字的编码多有歧义之处,比如XML和CSV不能区分数字和字符串,JSON虽然区分字符串和数字,但是不区分整数和浮点数,而且不能指定精度,处理大量数据时,这个问题更严重了;

没有强制模型约束,实际操作中往往只能采用文档方式来进行约定,这可能会给调试带来一些不便。 由于JSON在一些语言中的序列化和反序列化需要采用反射机制,所以性能比较差;

二进制编码:

实现可以有很多种,TLV 编码 和 Varint 编码

2.5 编解码层—二进制编码

这里我们可以看到他的第一个byte是类型,主要用来表示是string还是int还是list等等。

这里不写key的字符串了,比如上面的userName,favoriteNumber等等,

取而代之的是一个field tag的东西,这个会设置成1,2,3和上面的schema中key字符串前面的数字,也就是用这里来取代了具体的key值,从而减小的总体的大小,

这里打包后压缩到 59个字节 TLV编码结构简单清晰,并且扩展性较好,但是由于增加了Type和Length两个冗余信息,有额外的内存开销,特别是在大部分字段都是基本类型的情况下有不小的空间浪费。

2.6 编解码层— 选型

兼容性:

移动互联时代,业务系统需求的更新周期变得更快,新的需求不断涌现,而老的系统还是需要继续维护。如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,而不影响老的服务,这将大大提供系统的灵活度。

通用性有两个层面的意义:

第一、技术层面,序列化协议是否支持跨平台、跨语言。

		如果不支持,在技术层面上的通用性就大大降低了。 

第二、流行程度,序列化和反序列化需要多方参与,很少人使用的协议往往意味着昂贵的学习成本;另一方面,流行度低的协议,往往缺乏稳定而成熟的跨语言、跨平台的公共包。

性能:

第一、空间开销(Verbosity),

序列化需要在原有的数据上加上描述字段,为反序列化解析之用。如果序列化过程引入的额外开销过高,可能会导致过大的网络,磁盘等各方面的压力。

对于海量分布式存储系统,数据量往往以TB为单位,巨大的的额外空间开销意味着高昂的成本。

第二、时间开销(Complexity),复杂的序列化协议会导致较长的解析时间,这可能会使得序列化和反序列化阶段成为整个系统的瓶颈。

2.7 协议层 TTransport

2.8 协议层— 概念

协议是双方确定的交流语义,比如:我们设计一个字符串传输的协议,它允许客户端发送一个字符串,服务端接收到对应的字符串。

这个协议很简单,首先发送一个4字节的消息总长度,然后再发送1字节的字符集charset长度,接下来就是消息的payload,字符集名称和字符串正文。

特殊结束符:过于简单,对于一个协议单元必须要全部读入才能够进行处理,除此之外必须要防止用户传输的数据不能同结束符相同,否则就会出现紊乱

HTTP 协议头就是以回车(CR)加换行(LF)符号序列结尾。

变长协议:一般都是自定义协议,有 header 和 payload 组成,会以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度,使用比较广泛

2.9 协议构造

LENGTH 字段 32bits,包括数据包剩余部分的字节大小,不包含 LENGTH 自身长度

HEADER MAGIC 字段16bits,值为:0x1000,用于标识 协议版本信息,协议解析的时候可以快速校验 FLAGS 字段 16bits,为预留字段,暂未使用,默认值为 0x0000

SEQUENCE NUMBER 字段 32bits,表示数据包的 seqId,可用于多路复用,最好确保单个连接内递增

HEADER SIZE 字段 16bits,等于头部长度 字节数/4,头部长度计算从第14个字节开始计算,一直到 PAYLOAD 前(备注:header 的最大长度为 64K)

PROTOCOL ID 字段 uint8 编解码方式,

取值有:~ ProtocolIDBinary = 0 ProtocolIDCompact = 2 这两种

NUM TRANSFORMS 字段 uint8 编码,表示 TRANSFORM 个数

TRANSFORM ID 字段 uint8 编码,具体取值参考下文,表示压缩方式 zlib or snappy

INFO ID 字段 uint8 编码,具体取值参考下文,用于传递一些定制的 meta 元数据 信息

PAYLOAD 消息内容

2.10协议解析

2.11 网络通信层 Network IO

2.12 Sockets API

套接字编程中的客户端必须知道两个信息:服务器的 IP 地址,以及端口号

socket函数创建一个套接字,bind 将一个套接字绑定到一个地址上。

listen 监听进来的连接,放到队列里,backlog的含义有点复杂,这里先简单的描述:指定挂起的连接队列的长度,当客户端连接的时候,服务器可能正在处理其他逻辑而未调用accept接受连接,此时会导致这个连接被挂起,内核维护挂起的连接队列,backlog则指定这个队列的长度,accept函数从队列中取出连接请求并接收它,然后这个连接就从挂起队列移除。如果队列未满,客户端调用connect马上成功,如果满了可能会阻塞等待队列未满(实际上在Linux中测试并不是这样的结果,这个后面再专门来研究)。

Linux的backlog默认是128,通常情况下,我们也指定为128即可。

connect 客户端向服务器发起连接,accept 接收一个连接请求,如果没有连接则会一直阻塞直到有连接进来。得到客户端的fd之后,就可以调用read, write函数和客户端通讯,读写方式和其他I/O类似

read 从fd读数据,socket默认是阻塞模式的,如果对方没有写数据,read会一直阻塞着:

write 写fd写数据,socket默认是阻塞模式的,如果对方没有写数据,write会一直阻塞着:

socket 关闭套接字,当另一端socket关闭后,这一端读写的情况: 尝试去读会得到一个EOF,并返回0。 尝试去写会触发一个SIGPIPE信号,并返回-1和errno=EPIPE,SIGPIPE的默认行为是终止程序,所以通常我们应该忽略这个信号,避免程序终止。

如果这一端不去读写,我们可能没有办法知道对端的socket关闭了。

2.13 网络库

02 小结

03 关键指标

3.1稳定性-保障策略

熔断:

一个服务 A 调用服务 B 时,服务 B 的业务逻辑又调用了服务 C,而这时服务 C 响应超时了,由于服务 B 依赖服务 C,C 超时直接导致 B 的业务逻辑一直等待,而这个时候服务 A 继续频繁地调用服务 B,服务 B 就可能会因为堆积大量的请求而导致服务宕机,由此就导致了服务雪崩的问题

限流: 当调用端发送请求过来时,服务端在执行业务逻辑之前先执行检查限流逻辑,如果发现访问量过大并且超出了限流条件,就让服务端直接降级处理或者返回给调用方一个限流异常

超时: 当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,避免浪费资源 从某种程度上讲超时、限流和熔断也是一种服务降级的手段

3.2 稳定性 - 请求成功率

注意,因为重试有放大故障的风险,首先,重试会加大直接下游的负载。

如上图,假设 A 服务调用 B 服务,重试次数设置为 r(包括首次请求),当 B 高负载时很可能调用不成功,这时 A 调用失败重试 B ,B 服务的被调用量快速增大,最坏情况下可能放大到 r 倍,不仅不能请求成功,还可能导致 B 的负载继续升高,甚至直接打挂。 防止重试风暴,限制单点重试和限制链路重试

3.3 稳定性 - 长尾请求

长尾请求一般是指明显高于均值的那部分占比较小的请求。 业界关于延迟有一个常用的P99标准, P99 单个请求响应耗时从小到大排列,顺序处于99%位置的值即为P99 值,那后面这 1%就可以认为是长尾请求。在较复杂的系统中,长尾延时总是会存在。造成这个的原因非常多,常见的有网络抖动,GC,系统调度。 我们预先设定一个阈值 t3(比超时时间小,通常建议是 RPC 请求延时的 pct99 ),当 Req1 发出去后超过 t3 时间都没有返回,那我们直接发起重试请求 Req2 ,这样相当于同时有两个请求运行。然后等待请求返回,只要 Resp1 或者 Resp2 任意一个返回成功的结果,就可以立即结束这次请求,这样整体的耗时就是 t4 ,它表示从第一个请求发出到第一个成功结果返回之间的时间,相比于等待超时后再发出请求,这种机制能大大减少整体延时。

3.2 稳定性 - 请求成功率

04 企业实践

非常感谢您阅读到这里,创作不易!如果这篇文章对您有帮助,希望能留下您的点赞👍 关注💖 收藏 💕评论💬感谢支持!!!

听说 三连能够给人 带来好运!更有可能年入百w,进入大厂,上岸

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

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

相关文章

Forrester首次面向中国的开源报告:阿里云在云原生领域开源布局最全面

Forrester 于近期发布了《Navigate The Cloud-Native Ecosystem In China, 2023》,报告概述了中国云原生领域的开源项目对构建云原生生态的促进作用,这些开源项目正深刻影响着企业的技术决策者以何种策略拥抱云原生这一现代 IT 基础设施的核心。 报告表…

SMC状态机 讲解2 从模型到SMC

SMC状态机 讲解2 从模型到SMC 1、实例化有限状态机(FSM)2、简单转换 Simple Transition3、外部环回转换 External Loopback Transition4、内部环回转换 Internal Loopback Transition5、转换动作6、转换Guard7、转换参数8、Entry 和 Exit动作9、Push 转换10、Pop转换…

AI加持,创意设计效率百倍提升,探秘背后的数字化魔法

在当今创新潮流不断涌现的时代,人工智能正以惊人的速度和深度赋能各行各业,食品包装设计界也已来到了一个“拼创意、拼二创和拼审美”的时代。有了AI的加入,设计界正迎来一股AI创意风暴,不仅颠覆了设计流程,更为食品包…

机器学习教程(非常详细)从零基础入门到精通,看完这一篇就够了

一、机器学习的定义 从广义上来说,机器学习是一种能够赋予机器学习的能力以此让它完成直接编程无法完成的功能的方法。但从实践的意义上来说,机器学习是一种通过利用数据,训练出模型,然后使用模型预测的一种方法。 “训练”与“…

在leangoo免费敏捷工具中如何批量设置成员权限

Leangoo领歌是一款永久免费的专业敏捷开发管理工具,提供端到端敏捷研发管理解决方案,涵盖敏捷需求管理、任务协同、进展跟踪、缺陷管理、统计度量等。 包括小型团队敏捷开发,规模化敏捷SAFe,Scrum of Scrums大规模敏捷。其功能/解…

java包的package-info.java文件

Java包的下面可以放一个package-info.java文件,在这个文件中声明包,在注释中增加包的介绍信息。这样javadoc工具就可以优先从这个文件中获取包的介绍信息。 例如Java工程,在包com.thb下面有package-info.java文件: package-inf…

优秀产品奖!移远5G RedCap模组,让5G真正“轻”下来

8月24日,在通信世界全媒体主办的“5G RedCap技术与物联网应用创新研讨会”上,“5G RedCap优秀产品和解决方案”获奖名单发布,移远通信5G RedCap模组Rx255C系列以其在创新性、实用性、经济性、成熟性等方面的综合领先优势,获此殊荣…

Spring Boot进阶(58):集成PostgreSQL数据库及实战使用 | 万字长文,超级详细

1. 前言🔥 PostgreSQL是一种广泛使用的开源关系型数据库,具有可靠性高、性能优异、拥有丰富的数据类型和扩展等优点,越来越多的企业和开发者开始使用它来存储和管理数据。而Spring Boot是一种快速开发的框架,可以简化开发过程并提…

照片怎么转换成pdf?几种照片转pdf方法看一看

照片怎么转换成pdf?照片转换成PDF是一个非常有用的技能,可以将多张照片合并为一个文件,方便保存和分享。现在也有很多方法可以将照片转换为PDF,下面就给大家介绍几种转换方法。 转换方法一:迅捷PDF转换器 这是一款功能…

GPT---1234

GPT:《Improving Language Understanding by Generative Pre-Training》 下载地址:https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdfhttps://cdn.openai.com/research-covers/language-unsupervised/language_understa…

前端遇到困扰怎么办?10年前端在线帮您解决问题,只需一杯下午茶

前端遇到困扰怎么办?10年前端在线帮您解决问题,只需一杯下午茶

Ceph入门到精通-基于ECMP的多活负载均衡策略

本文简单介绍一下,如何基于ECMP,使用QuaggaLVSKeepalived构建多活负载均衡方案 1. 背景介绍 负载均衡,主要用于大规模分布式集群下,提供高性能服务。为了给负载均衡器提供高可用,一般利用主备或者主主模式实现。主备模…

前端三剑客入门一文解决

文章目录 HTML快速开发网站Flask页面结构标签基础标签超链接图片列表下拉框表格input系列多行文本form表单 网络请求HTML案例 CSSCSS盒模型CSS样式定义CSS选择器 CSS样式使用1. 在标签上直接写2. 在head标签中写3.写到css文件中 标签样式1. 高度和宽度2. 块级和行内标签3.字体设…

原生微信小程序使用 wxs;微信小程序使用 vant-weapp组件

1.原生微信小程序使用 wxs 1.内嵌 WXS 脚本 2. 定义外链 wxs 3. 使用外连wxs 在这里插入图片描述 2. 微信小程序使用 vant weapp 1.安装步骤 2. 安装包管理(package.json)文件的方法 操作顺序 :文档地址 如果使用 typescript 需要操作步骤3,否则不…

go语言kafka入门

消息队列:一种基于异步通信的解耦机制,用于在应用程序或系统组件之间传递消息和数据 消息队列相关概念: 生产者(Producer):生成并发送消息到消息队列中的应用程序或系统组件。 消费者(Consumer&…

Node基础--WebStorm整合Node

通过上面的课程,我们对Node有了一个初步的体验,下面我们就把Node和开发工具WebStrom进行整合。 1.安装开发工具WebStrom (1).查找官方下载地址: https://www.jetbrains.com/webstorm/ (2).下载之后开始点击安装 (3).设置安装路径。注意:安装路径自定义 (4).按照自己的…

vue 简单实验 自定义组件 独立模块

1.概要 2.代码 2.1 const Counter {data() {return {counter: 0}},template:<div>Counter: {{ counter }}</div> }export default Counter 2.2 import Counter from ./t2.jsconst app Vue.createApp({components: {component-a: Counter} })app.mount(#count…

C - 滑动窗口 /【模板】单调队列

Description 有一个长为 n 的序列 a&#xff0c;以及一个大小为 k 的窗口。现在这个从左边开始向右滑动&#xff0c;每次滑动一个单位&#xff0c;求出每次滑动后窗口中的最大值和最小值。 例如&#xff1a; The array is [1,3,−1,−3,5,3,6,7] and k3。 Input 输入一共有…

ssm农产品仓库管理系统系统源码和论文

ssm农产品仓库管理系统系统源码和论文064 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 选题的背景 中国是一个农产品生产的大国&#xff0c;可利用的农产品资源相对贫乏&#xff0c;传统的单纯依靠大量物质…

k3s or RKE2 helm安装报错dial tcp 127.0.0.1:8080: connect: connection refused

1.报错&#xff1a; Error: INSTALLATION FAILED: Kubernetes cluster unreachable: Get "http://127.0.0.1:8080/version": dial tcp 127.0.0.1:8080: connect: connection refused 2.问题原因&#xff1a; 1.因为helm默认使用k8s的配置文件&#xff0c;默…