怎样实现RPC框架

news2024/12/23 12:44:50

随着微服务架构的盛行,远程调用成了开发微服务必不可少的能力,RPC 框架作为微服务体系的底层支撑,也成了日常开发的必备工具。当下,RPC 框架已经不仅是进行远程调用的基础工具,还需要提供路由、服务发现、负载均衡、容错等能力。

基础能力设计

RPC(Remote Procedure Call)远程过程调用,顾名思义最基本的能力当然是远程调用一个过程。放到今天的面向对象的语言中,其实就是调用一个远程的方法。在远程我们必须先定义这个方法,然后才可以通过 RPC 框架调用该方法,远程调用不仅可以传参数、获取到返回值,
还可以捕捉调用过程中的异常。RPC 让远程调用就像本地调用一样。假设我们实现了一个rpc对象,其中的invoke方法可以实现远程调用。下面这段伪代码在调用远程的greetings方法(RPC 调用),并向远程方法传递参数arg1``arg2,然后再接收到远程的返回值。

var result = rpc.invoke("greetings", arg1, arg2,
...)

这段程序将本地看作 一个 RPC 的客户端,将远程看作一个 RPC 的服务端。如下图所示:
在这里插入图片描述
服务 A 发起远程方法调用,RPC 客户端通过某种协议将请求发送给服务 B,服务 B 解析请求,进行本地方法的调用,将结果返回到服务 B 的 RPC 服务端,最终返回到服务 A。对服务 A 来说,调用的是一个函数,从接口到返回值的设计,和调用本地函数并没有太大的差别。当然,程序员不能完全忽略这是一次远程方法调用,因为远程调用的开销较大。如果程序员没有意识到调用远程方法有网络开销,就可能会写出下面这段程序:

for(int i = 0; i < 1000000; i++) {
 rpc.invoke(...)
}

之所以写出上面的程序,是因为你(程序员)没有意识到 rpc.invoke 是一次远程调用。在实际的操作过程中,rpc.invoke可能被封装到了某个业务方法中,程序员调用的时候便容易忽视这是一次远程操作。所以 RPC 调用时就要求你(程序员)对性能有清晰的认识。

多路复用的优化

RPC 提供的是远程方法的调用,但本质上是数据的传递,传递数据有一个最基本的问题要处理,就是提升吞吐量(单位时间传递的数据量)。如果为每个远程调用(请求)建立一个连接,就会造成资源的浪费,因此通常我们会考虑多个请求复用一个连接,叫作多路复用。在具体实现多路复用的时候,也会有不同的策略。假设我们要发送数据 A、B、C、D,那么一种方式是建立一个连接,依次将 A、B、C、D 发过去,就像下图这样:
在这里插入图片描述
在这种结构中,利用一个连接顺序发送 A、B、C、D 将多个请求放入一个连接的方式,节省了多次握手、挥手的时间,但是由于 ABCD 不是真的并行发送,而是顺序发送,当其中某个请求的体积较大时,容易阻塞其他请求。比如下图这种情况:
在这里插入图片描述
在 A 较大的时候,B,C,D 就只能等 A 完全传送完成才能发生传送。这样的模型对于 RPC 请求/响应大小不平均的网络不太友好,体积小的请求/响应可能会因为一些大体积的请求/响应而延迟。因此还有另一种常见的多路复用方案,就是将 A、B、C、D 切片一起传输,如下图所示:
在这里插入图片描述
上图中,我们用不同颜色代表不同的传输任务。采用顺序传输方案将 A、B、C、D 用一个连接传输节省了握手、挥手的成本。切片传输的方案在这之上,将数据切片可以保证大、小任务并行,不会因为大任务阻塞小任务。另外还有一个需要考虑的点,是单个 TCP 连接的极限传输速度受到窗口大小、缓冲区等因素的制约,不一定可以用满网络资源。如果传输量特别大的时候,有可能需要考虑提供多个连接,每个连接再去考虑多路复用的情况。

调用约定和命名

接下来,我们一起思考下服务的命名。远程调用一个函数,命名空间+类名+方法名是一个比较好的选择,简而言之,每个可以远程调用的方法就是一个字符串。比如远程调用一个支付服务对象 PayService 的 pay 方法,命名空间可能是 trade.payment,对象名称是PayService,方法名称是 pay。组合起来可以是一个完整的字符串,例如用 # 分割:trade.payment#PayService#pay。在进行远程调用的时候,给远程方法命名是调用约定的一部分。我们通过调用命名空间下完整的名称调用远程方法。在面向对象的语言中,还有一种常见的做法是先不具体指定调用的方法,而是先创造一个远程对象的实例。比如上面例子中我们先通过 RPC 框架构造一个PayService 对象的实例。这里会用到一些特别的编程技巧,比如代理设计模式、动态接口生成等。不过归根结底,我们调用的本质就是字符串名称。而实现这个调用,你需要知道两件事情:
IP 是多少,也就是方法在哪台机器上调用;
端口是多少,也就是哪个服务提供这个调用。

注册和发现

调用的时候,我们需要根据字符串(命名)去获取 IP 和端口(机器和服务)。机器可以是虚拟机、容器、实体机,也可以是某个拥有虚拟网卡的代理。在网络的世界中,需要的只是网络接口和 IP 地址。而操作系统区分应用需要的是端口。所以,在调用过程中,我们需要的是一个注册表,存储了字符串和 IP + 端口的对应关系。聪明的同学可能马上会想到,用 Redis 的hash对象存储这个对应关系就很不错。当我们上线一个服务的时候,就在 Redis 的某个hash对象中存储它和它对应的 IP 地址 + 端口列表。为什么是存一个列表?因为一个服务可能由多个机器提供。通常我们将写这个hash对象的过程,也就是服务被记录的过程称作注册。我们远程调用一个RPC 服务的时候,调用端提供的是 RPC 服务的名称(例如:命名空间+对象+方法),根据名称查找到提供服务的 IP + 端口清单并指定某个 IP + 端口(提供服务)的过程称作发现。当然,我们不能就这样简单理解成:注册就是写一个共享的哈希表,发现就是查哈希表再决定服务的响应者。在实际的设计中,要考虑的因素会更多。比如基于 Redis 的实现,如果所有 RPC 调用都需要去 Redis 查询,会造成负责发现的中间件压力较大。实际的操作过程中,往往会增加缓存。也就是 RPC 调用者会缓存上一次调用的 IP+ 端口。但是这样设计,缓存又可能会和注册表之间产生数据不一致的问题。这个时候,可以考虑由分布式共识服务比如 ZooKeeper 提供订阅,让 RPC 调用者订阅到服务地址的变更,及时更新自己的缓存。设计注册和发现两个功能的最大的价值是让客户端不再需要关注服务的部署细节,这样方便在全局动态调整服务的部署策略。

负载均衡的设计

在设计 RPC 框架的时候,负载均衡器的设计往往需要和 RPC 框架一起考虑。因为 RPC 框架提供了注册、发现的能力,提供发现能力的模块本身就是一个负载均衡器。因此负载均衡可以看作发现模块的一个子组件。请求到达 RPC 的网关(或某个路由程序)后,发现组件会提供服务对应的所有实例(IP + 端口),然后负载均衡算法会指定其中一个响应这个请求。

可用性和容灾

当一个服务实例崩溃的时候(不可用),因为有发现模块的存在,可以及时从注册表中删除这个服务实例。只要服务本身有足够多的实例,比如多个容器而且部署在不同的机器上,那么完全不可能用的风险会大大降低。当然,可用性是不可能 100% 实现的。另外,注册表和 RPC 调用者之间必然存在不一致现象,而且注册表的更新本身也可能滞后。比如确认一个服务有没有崩溃,可能需要一个心跳程序持续请求这个服务,因此 RPC 的调用者如果调用到一个不存在的服务,或者调用到一个发生崩溃的服务,需要自己重新去发现组件申请新的服务实例(地址 + 端口)。如果遇到临时访问量剧增,需要扩容的场景。这个时候只需要上线更多的容器,并且去注册即可。当然这要求部署模块和注册模块之间有较高的协同,这块可以用自动化脚本衔接。

总结

总结下,设计一个 RPC 框架最基础的能力就是实现远程方法的调用。这里需要一个调用约定,比如怎么描述一个远程的方法,发送端怎么传递参数,接收方如何解析参数?如果发生异常应该如何处理?具体来说,这些事情都不难实现,只是比较烦琐。其实不仅仅在 RPC 调用时有调用约定,编译器在实现函数调用的时候,也会有调用约定。另外,还有一些在 RPC 基础上建立起来的更复杂、更体系化的约定,比如说面向服务架构(SOA)。在实现了基本调用能力的基础上,接下来就是提供服务的注册、发现能力。有了这两个能力,就可以向客户端完全屏蔽服务的部署细节,并衍生出容灾、负载均衡的设计。当然,程序员还需要思考底层具体网络的传输问题。如果用 TCP 要思考多路复用以及连接数量的问题;如果是 UDP,需要增加对于可靠性保证的思考。如果使用了消息队列,还需要考虑服务的幂等性设计等。

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

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

相关文章

两种QGraphicsItem方式实现橡皮擦功能(矩形选中框)

方法一&#xff1a;继承QGraphicsItem实现橡皮擦功能。&#xff08;gif中红色矩形框&#xff09; 方法二&#xff1a;继承QGraphicsRectItem实现的橡皮擦功能。&#xff08;gif中蓝色矩形框&#xff09; 通过以上GIF可以看出两款橡皮擦都具有位置拖动和大小拖动的功能&#xff…

Nacos必知必会:这些知识点你一定要掌握!

前言 Nacos 是一个开源的服务发现、配置管理和服务治理平台&#xff0c;是阿里巴巴开源的一款产品。 Nacos 可以帮助开发者更好地管理微服务架构中的服务注册、配置和发现等问题&#xff0c;提高系统的可靠性和可维护性。 本文将介绍 Nacos 的必知必会知识点&#xff0c;包括…

Zookeeper基础和简单使用

安装与配置 概念 基于观察者模式设计的分布式服务管理框架&#xff0c;负责存储和管理大家都关心数据&#xff0c;然后接受观察者的注册&#xff0c;一单这些数据的这状态发生了变化&#xff0c;Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应 特点…

当JS遇到加密:解密你的JS代码!

作为一名程序员&#xff0c;我们经常会遇到各种加密算法&#xff0c;比如常见的AES、RSA、MD5等等&#xff0c;但是今天我想和大家聊一聊一个日常生活中比较常见的加密方式——JavaScript加密。 在我们日常浏览网页时&#xff0c;经常会看到一些网站的JavaScript代码经过加密&…

【Redis】布隆过滤器原理与应用

文章目录 原理应用实战总结 布隆过滤器&#xff08;Bloom Filter&#xff09;是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。 它的优点是空间效率和查询时间都比一般的算法要好的多&#xff…

AJAX实现搜索联想 自动补全

分析: 1.想实现联想搜索需要数据库的数据支撑,需要进行模糊查询,搜索出所有包含用户输入的关键字信息,并将这些信息都反馈到前端,简化用户输入,从而提高用户的体验。 2.为了提高用户的使用体验&#xff0c;整个页面不能全部刷新&#xff0c;需要局部刷新&#xff0c;为此需要…

Python中的各种报错-一般错误

目录 ValueError: check_hostname requires server_hostname missing 1 required positional argument: self xxx is not a package libpng warning: iCCP: cHRM chunk does not match sRGB check_hostname requires server_hostname python 安装第三方库&#xff0c;超时…

MQTTC数据桥接上云

[小 迪 导 读]&#xff1a;在工业物联网蓬勃发展的背景下&#xff0c;私有化部署已经不能满足当前的发展趋势了&#xff0c;因此dgiot在原有基础上进行创新&#xff0c;将私有化部署的区域数控一体机上的数据通过mqtt桥接的方式上传到云服务器上&#xff0c;完成数据的实时同步…

Baumer工业相机堡盟工业相机软件CameraExplorer常见功能使用说明一

Baumer工业相机堡盟工业相机软件CameraExplorer常见功能使用说明一 Baumer工业相机Baumer工业相机图像采集功能Baumer工业相机图像基本参数设置 Baumer工业相机 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机&#xff0c;可用于各种应用场景&#xff0c;如物体检测、…

【前端提效】-- Chrome浏览器开发者工具使用技巧

介绍一下 DevTools 的一些好用的技巧&#xff0c;它能够很好地帮助你提高生产力和解决问题的能力。 1、打开命令行 或者使用&#xff1a;快捷键 Ctrl Shift P (Mac&#xff1a; ⌘ Shift P ) 命令行可以做很多事情&#xff0c;包括但不限于截图、更换主题等 2、控制 DevT…

暑假线上兼职:300-500元/小时,安利一个大学生也能月入8K的线上兼职

在后台经常收到这样的留言&#xff1a; 快接近暑假了&#xff0c;有没有线上兼职推荐&#xff1f; 如何提升自己的眼界和能力&#xff0c;为之后的职场铺路&#xff1f; 不知道有多少朋友是想提升自己获取资源信息的速度&#xff0c;发展自己的爱好&#xff0c;或者增加第二收入…

用友U8增加查询条件

1、检查要增加的条件是否在该表单中&#xff1b; 2、在查询条件中增加查询条件的管理方案 3、参照ID就是要参照的表 4、数据源&#xff0c;在要增加的表单数据库中查询该字段名&#xff0c;进行增加即可。 select * from purbillvouch where cpbvcode 0000000312 --PurBillV…

调用legend资源,生成地图图例

作者&#xff1a;lly 文章目录 前言一、接口详情二、具体实现三、结果展示 前言 最近很多小伙伴资源关于iServer图例的问题&#xff0c; 接下来我们就来介绍下如何使用iServer legend资源&#xff0c;生成地图图例 一、接口详情 请求示例 {"layerLegends": [{&quo…

达索的天线设计和仿真软件Antenna Magus 2023版本下载与安装配置教程

目录 前言一、Antenna Magus安装二、使用配置总结 前言 Antenna Magus软件是一款用于天线设计和仿真的软件&#xff0c;提供了一个全面的设计工具集&#xff0c;帮助工程师优化天线设计&#xff0c;同时减少设计周期。 Antenna Magus的主要特点&#xff1a; ——高级天线库&…

如何用 ChatGPT 帮你自动分析数据?

误判 好几天之前&#xff0c;我就在 ChatGPT 选单里看到了 Code Interpreter。它正在灰度测试中 —— 先给一部分用户试用&#xff0c;如果反响不错并做了一定改进&#xff0c;就能推广给更多用户。 可惜当时我没能正确理解它的含义&#xff0c;犯了一个大错误 —— 望文生义。…

ChatGPT 上线联网和插件功能;投资者看好新版搜索引擎

&#x1f680; ChatGPT 上线联网和插件功能 OpenAI宣布将在这周推出联网和插件功能&#xff0c;位于Alpha和Beta通道的ChatGPT Plus用户都可使用70多个上线的插件。 更新意味着ChatGPT将利用最新的信息和资讯为使用者提供服务。 上线的ChatGPT插件种类涵盖了行程安排助理、代…

Android开发-Android常用组件-Date Time组件

4.11 Date & Time组件 1.TextClock(文本时钟) TextClock是在Android 4.2(API 17)后推出的用来替代DigitalClock的一个控件&#xff01; TextClock可以以字符串格式显示当前的日期和时间&#xff0c;因此推荐在Android 4.2以后使用TextClock。 这个控件推荐在24进制的and…

[内网]RDP远程桌面密码凭证获取

文章目录 RDP保存凭据通过注册表查看当前主机本地连接过的目标机器记录查看当前主机保存的RDP凭据查看本地用户是否存有RDP密码文件通过密码文件获取guidMasterKey的值根据guidMasterKey找到对应Masterkey解密获取明文以上总结&#xff1a; 在授权渗透过程中&#xff0c;如果获…

PCB当中的跳线有什么作用

在PCB设计中&#xff0c;跳线是一种常见的连接方式。跳线是为了连接电路中的距离较远或无法直接连接的电路元件而设置的&#xff0c;其作用是非常重要的。在本文中&#xff0c;我们将探讨PCB中跳线的作用和应用。 一、什么是跳线 跳线是一种特殊的电路连接方式&#xff0c;它是…

EXCEL 0开头的数据处理

方法一&#xff1a;从数据库中存为csv 再新建一个EXCEL 数据——从文本/CSV 方法二&#xff1a; 在数据库里面加A&#xff0c;在EXCEL里面将A替换成 上单引号 ‘