![](https://img-blog.csdnimg.cn/20201014180756757.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
RPC 实战与核心原理
文章平均质量分 91
RPC 实战与核心原理
_Rye_
左手代码右手诗
一行代码一行诗
展开
-
RPC框架代码实例详解
今天我们剖析了一款开源的 RPC 框架的代码,主要通过服务端启动流程、调用端启动流程、RPC 调用流程这三大流程来将 RPC 框架的核心模块以及核心类串联起来。在服务端的启动流程中,核心工作就是创建和开启 Exporter 对象。原创 2023-11-14 18:11:57 · 174 阅读 · 0 评论 -
24 | 如何在线上环境里兼容多种RPC协议?
在我们日常开发的过程中,最难的环节不是从 0 到 1 完成一个新应用的开发,而是把一个老应用通过架构升级完成从 70 分到 80 分的跳跃。因为在老应用升级的过程中,我们不仅需要考虑既有的功能逻辑,也需要考虑切换到新架构上的成本,这就要求我们在设计新架构的时候要考虑如何让老应用能够平滑地升级,就像在 RPC 里面支持多协议一样。在 RPC 里面支持多协议,不仅能让我们更从容地推进应用 RPC 的升级,还能为未来在 RPC 里面扩展新协议奠定一个良好的基础。原创 2023-11-14 17:46:38 · 50 阅读 · 0 评论 -
23 | 如何在没有接口的情况下进行RPC调用?
今天我们主要讲解了如何在没有接口的情况下进行 RPC 调用,泛化调用的功能可以实现这一目的。这个功能的实现原理,就是 RPC 框架提供统一的泛化调用接口(GenericService),调用端在创建 GenericService 代理时指定真正需要调用的接口的接口名以及分组名,通过调用 GenericService 代理的 $invoke 方法将服务端所需要的所有信息,包括接口名、业务分组名、方法名以及参数信息等封装成请求消息,发送给服务端,实现在没有接口的情况下进行 RPC 调用的功能。原创 2023-11-14 17:21:22 · 101 阅读 · 0 评论 -
22 | 动态分组:超高效实现秒级扩缩容
在[第 16 讲],我们讲了分组后带来的收益,它可以帮助服务提供方实现调用方的隔离。但是因为调用方流量并不是一成不变的,而且还可能会因为突发事件导致某个分组的流量溢出,而在整个大集群还有富余能力的时候,又因为分组隔离不能为出问题的集群提供帮助。为了解决这种突发流量的问题,我们提供了一种更高效的方案,可以实现分组的快速扩缩容。原创 2023-11-14 16:43:57 · 102 阅读 · 0 评论 -
21 | 流量回放:保障业务技术升级的神器
保障线上应用的稳定,是我们研发同学每天都在努力耕耘的一件事,不管是通过应用架构升级,还是修复现有问题的方式。实际情况就是我们不仅要保障已有业务的稳定,还需要快速去完成各种新业务的需求,这期间我们的应用代码就会经常发生变化,而发生变化后就可能会引入新的不稳定因素,而且这个过程会一直持续不断发生。为了保障应用升级后,我们的业务行为还能保持和升级前一样,我们在大多数情况下都是依靠已有的 TestCase 去验证,但这种方式在一定程度上并不是完全可靠的。原创 2023-11-14 15:44:41 · 168 阅读 · 0 评论 -
20 | 详解时钟轮在RPC中的应用
这个问题也不难解决,我们只要找到一种方式,减少额外的扫描操作就行了。比如我的一批定时任务是 5 秒之后执行,我在 4.9 秒之后才开始扫描这批定时任务,这样就大大地节省了 CPU。这时我们就可以利用时钟轮的机制了。我们先来看下我们生活中用到的时钟。很熟悉了吧,时钟有时针、分针和秒针,秒针跳动一周之后,也就是跳动 60 个刻度之后,分针跳动 1 次,分针跳动 60 个刻度,时针走动一步。而时钟轮的实现原理就是参考了生活中的时钟跳动的原理。原创 2023-11-14 15:00:55 · 36 阅读 · 0 评论 -
19 | 分布式环境下如何快速定位问题?
今天我们讲解了在分布式环境下如何快速定位问题。这里面的难点就是分布式系统有着较为复杂的依赖关系,我们很难判断出是哪个环节出现的问题,而且在大型的分布式系统中,往往会有跨部门、跨团队合作的情况,在排查问题的时候会面临非常高的沟通成本。为了在分布式环境下能够快速地定位问题,RPC 框架应该对框架自身的异常进行详细地封装,每类异常都要有明确的异常标识码,并将其整理成一份简明的文档,异常信息中要尽量包含服务接口名、服务分组、调用端与服务端的 IP,以及产生异常的原因等信息,这样对于使用方来说就非常便捷了。原创 2023-11-14 14:09:37 · 195 阅读 · 0 评论 -
18 | 安全体系:如何建立可靠的安全体系?
安全问题在任何一个领域都很重要,但又经常被我们忽视,只有每次出安全事故后,我们才会意识到安全防护的重要性。所以在日常写代码的过程中,我们一定要保持一个严谨的态度,防止细小错误引入线上安全问题。虽然 RPC 经常用于解决内网应用之间的调用,内网环境相对公网也没有那么恶劣,但我们也有必要去建立一套可控的安全体系,去防止一些错误行为。对于 RPC 来说,我们所关心的安全问题不会有公网应用那么复杂,我们只要保证让服务调用方能拿到真实的服务提供方 IP 地址集合,且服务提供方可以管控调用自己的应用就够了。原创 2023-11-14 11:28:22 · 43 阅读 · 0 评论 -
17 | 异步RPC:压榨单机吞吐量
今天我们主要讲解了如果通过 RPC 的异步去压榨单机的吞吐量。影响到 RPC 调用的吞吐量的主要原因就是服务端的业务逻辑比较耗时,并且 CPU 大部分时间都在等待而没有去计算,导致 CPU 利用率不够,而提升单机吞吐量的最好办法就是使用异步 RPC。RPC 框架的异步策略主要是调用端异步与服务端异步。原创 2023-11-14 10:51:04 · 106 阅读 · 0 评论 -
思考题答案合集
调用端为每一个消息生成一个唯一的消息 ID,它收到服务端发送回来的响应消息如果是同一消息 ID,那么调用端就可以认为,这条响应消息是之前那条请求消息的响应消息。在整个 RPC 调用的过程中,从动态代理到负载均衡之间还有一系列的操作,如果你研究过开源的 RPC 框架,你会发现在调用端发送请求消息之前还会经过过滤链,对请求消息进行层层的过滤处理,之后才会通过负载均衡选择服务节点,发送请求消息,而异常重试操作就发生在过滤链处理之后,调用负载均衡选择服务节点之前,这样的重试是可以减少很多重复操作的。原创 2023-11-14 08:46:09 · 46 阅读 · 0 评论 -
16 | 业务分组:如何隔离流量?
今天我们通过一个道路划分的案例,引出了在 RPC 里面我们可以通过分组的方式人为地给不同的调用方划分出不同的小集群,从而实现调用方流量隔离的效果,保障我们的核心业务不受非核心业务的干扰。但我们在考虑问题的时候,不能顾此失彼,不能因为新加一个的功能而影响到原有系统的稳定性。其实我们不仅可以通过分组把服务提供方划分成不同规模的小集群,我们还可以利用分组完成一个接口多种实现的功能。正常情况下,为了方便我们自己管理服务,我一般都会建议每个接口完成的功能尽量保证唯一。原创 2023-11-13 22:58:53 · 129 阅读 · 0 评论 -
15 | 熔断限流:业务如何实现自我保护?
今天我们主要讲解了 RPC 框架是如何实现业务的自我保护。服务端主要是通过限流来进行自我保护,我们在实现限流时要考虑到应用和 IP 级别,方便我们在服务治理的时候,对部分访问量特别大的应用进行合理的限流;服务端的限流阈值配置都是作用于单机的,而在有些场景下,例如对整个服务设置限流阈值,服务进行扩容时,限流的配置并不方便,我们可以在注册中心或配置中心下发限流阈值配置的时候,将总服务节点数也下发给服务节点,让 RPC 框架自己去计算限流阈值;原创 2023-11-13 22:15:32 · 88 阅读 · 0 评论 -
14 | 优雅启动:如何避免流量打到没有启动完成的节点?
包括[第 11 讲] 在内,到今天为止,我们就已经把整个 RPC 里面的启停机流程都讲完了。就像前面说过的那样,虽然启停机流程看起来不属于 RPC 主流程,但是如果能在 RPC 里面把这些“微小”的工作做好,就可以让技术团队感受到更多的微服务带来的好处。另外,我们今天的两大重点——启动预热与延迟暴露,它们并不是 RPC 的专属功能,我们在开发其它系统时,也可以利用这两点来减少冷启动对业务的影响。原创 2023-11-13 22:05:26 · 85 阅读 · 0 评论 -
13 | 优雅关闭:如何避免服务停机带来的业务损失?
在 RPC 里面,关闭虽然看似不属于 RPC 主流程,但如果我们不能处理得很好的话,可能就会导致调用方业务异常,从而需要我们加入很多额外的运维工作。一个好的关闭流程,可以确保使用我们框架的业务实现平滑的上下线,而不用担心重启导致的问题。其实“优雅关闭”这个概念除了在 RPC 里面有,在很多框架里面也都挺常见的,比如像我们经常用的应用容器框架 Tomcat。Tomcat 关闭的时候也是先从外层到里层逐层进行关闭,先保证不接收新请求,然后再处理关闭前收到的请求。原创 2023-11-13 21:36:03 · 58 阅读 · 0 评论 -
12 | 异常重试:在约定时间内安全可靠地重试
今天我们讲解了 RPC 框架的重试机制,还有如何在约定时间内进行安全可靠地重试。这个机制是当调用端发起的请求失败时,如果配置了异常重试策略,RPC 框架会捕捉异常,对异常进行判定,符合条件则进行重试,重新发送请求。在重试的过程中,为了能够在约定的时间内进行安全可靠地重试,在每次触发重试之前,我们需要先判定下这个请求是否已经超时,如果超时了会直接返回超时异常,否则我们需要重置下这个请求的超时时间,防止因多次重试导致这个请求的处理时间超过用户配置的超时时间,从而影响到业务处理的耗时。原创 2023-11-13 19:05:50 · 59 阅读 · 0 评论 -
11 | 负载均衡:节点负载差距这么大,为什么收到的流量还一样?
先来简单地介绍下负载均衡。当我们的一个服务节点无法支撑现有的访问量时,我们会部署多个节点,组成一个集群,然后通过负载均衡,将请求分发给这个集群下的每个服务节点,从而达到多个服务节点共同分担请求压力的目的。负载均衡示意图负载均衡主要分为软负载和硬负载,软负载就是在一台或多台服务器上安装负载均衡的软件,如 LVS、Nginx 等,硬负载就是通过硬件设备来实现的负载均衡,如 F5 服务器等。负载均衡的算法主要有随机法、轮询法、最小连接法等。原创 2023-11-13 18:42:50 · 225 阅读 · 0 评论 -
10 | 路由策略:怎么让请求按照设定的规则发到不同的节点上?
在日常工作中,我们几乎每天都在做线上变更,每次变更都有可能带来一次事故,为了降低事故发生的概率,我们不光要从流程上优化操作步骤,还要使我们的基础设施能支持更低的试错成本。灰度发布功能作为 RPC 路由功能的一个典型应用场景,我们可以通过路由功能完成像定点调用、黑白名单等一些高级服务治理功能。在 RPC 里面,不管是哪种路由策略,其核心思想都是一样的,就是让请求按照我们设定的规则发送到目标节点上,从而实现流量隔离的效果。原创 2023-11-13 18:22:29 · 80 阅读 · 0 评论 -
09 | 健康检测:这个节点都挂了,为啥还要疯狂发请求?
这一讲分享了 RPC 框架里面的一个核心的功能——健康检测,它能帮助我们从连接列表里面过滤掉一些存在问题的节点,避免在发请求的时候选择出有问题的节点而影响业务。但是在设计健康检测方案的时候,我们不能简单地从 TCP 连接是否健康、心跳是否正常等简单维度考虑,因为健康检测的目的就是要保证“业务无损”,所以在设计方案的时候,我们可以加入业务请求可用率因素,这样能最大化地提升 RPC 接口可用率。正常情况下,我们大概 30S 会发一次心跳请求,这个间隔一般不会太短,如果太短会给服务节点造成很大的压力。原创 2023-11-13 17:22:59 · 199 阅读 · 0 评论 -
08 | 服务发现:到底是要CP还是AP?
今天分享了 RPC 框架服务发现机制,以及如何用 ZooKeeper 完成“服务发现”,还有 ZooKeeper 在超大规模集群下作为注册中心所存在的问题。通常我们可以使用 ZooKeeper、etcd 或者分布式缓存(如 Hazelcast)来解决事件通知问题,但当集群达到一定规模之后,依赖的 ZooKeeper 集群、etcd 集群可能就不稳定了,无法满足我们的需求。原创 2023-11-13 16:53:47 · 60 阅读 · 0 评论 -
07 | 架构设计:设计一个灵活的RPC框架
我们都知道软件开发的过程很复杂,不仅是因为业务需求经常变化,更难的是在开发过程中要保证团队成员的目标统一。我们需要用一种可沟通的话语、可“触摸”的愿景达成目标,我认为这就是软件架构设计的意义。但仅从功能角度设计出的软件架构并不够健壮,系统不仅要能正确地运行,还要以最低的成本进行可持续的维护,因此我们十分有必要关注系统的可扩展性。只有这样,才能满足业务变化的需求,让系统的生命力不断延伸。原创 2023-11-13 15:52:54 · 38 阅读 · 0 评论 -
06 | RPC实战:剖析gRPC源码,动手实现一个完整的RPC
这是我们基础篇的最后一讲,我们采用剖析 gRPC 源码的方式来学习如何实现一个完整的 RPC。当然整个 gRPC 的代码量可比这多得多,但今天的主要目就是想让你把前面所学的序列化、协议等方面的知识落实到具体代码上,所以我们这儿只分析了 gRPC 收发请求两个过程。实现了这两个过程,我们就可以完成一个点对点的 RPC 功能,但在实际使用的时候,我们的服务提供方通常都是以一个集群的方式对外提供服务的,所以在 gRPC 里面你还可以看到负载均衡、服务发现等功能。原创 2023-11-13 14:04:43 · 420 阅读 · 0 评论 -
05 | 动态代理:面向接口编程,屏蔽RPC处理流程
今天我们介绍了动态代理在 RPC 里面的应用,虽然它只是一种具体实现的技术,但我觉得只有理解了方法调用是怎么被拦截的,才能厘清在 RPC 里面我们是怎么做到面向接口编程,帮助用户屏蔽 RPC 调用细节的,最终呈现给用户一个像调用本地一样去调用远程的编程体验。既然动态代理是一种具体的技术框架,那就会涉及到选型。我们可以从这样三个角度去考虑:因为代理类是在运行中生成的,那么代理框架生成代理类的速度、生成代理类的字节码大小等等,都会影响到其性能——生成的字节码越小,运行所占资源就越小。原创 2023-11-13 11:54:24 · 91 阅读 · 0 评论 -
04 | 网络通信:RPC框架在网络通信上更倾向于哪种网络IO模型?
刚才讲阻塞 IO 的时候我讲到,系统内核处理 IO 操作分为两个阶段——等待数据和拷贝数据。等待数据,就是系统内核在等待网卡接收到数据后,把数据写到内核中;而拷贝数据,就是系统内核在获取到数据后,将数据拷贝到用户进程的空间中。网络IO读写流程应用进程的每一次写操作,都会把数据写到用户空间的缓冲区中,再由 CPU 将数据拷贝到系统内核的缓冲区中,之后再由 DMA 将这份数据拷贝到网卡中,最后由网卡发送出去。原创 2023-11-13 11:24:30 · 448 阅读 · 0 评论 -
03 | 序列化:对象怎么在网络中传输?
今天我们深入学习了什么是序列化,并介绍了如 JDK 原生序列化、JSON、Hessian 以及 Protobuf 等几种常见的序列化方式。除了这些基础知识之外,我们重点讲解了在 RPC 框架中如何去选择序列化协议,我们有这样几个很重要的参考因素,优先级从高到低依次是安全性、通用性和兼容性,之后我们会再考虑序列化框架的性能、效率和空间开销。这归根结底还是因为服务调用的稳定性与可靠性,要比服务的性能与响应耗时更加重要。原创 2023-11-13 09:59:16 · 460 阅读 · 0 评论 -
02 | 协议:怎么设计可扩展且向后兼容的协议?
我们人类区别于其他动物的一个很大原因,就是我们能够通过语言去沟通,用文字去沉淀文明,从而让我们能站在巨人的肩膀上成长,但为了保证我们记录的文字能够被其他人理解,我们必须通过符号去实现断句,否则就可能导致文字的意义被曲解,甚至闹出笑话。在 RPC 里面,协议的作用就类似于文字中的符号,作为应用拆解请求消息的边界,保证二进制数据经过网络传输后,还能被正确地还原语义,避免调用方跟被调用方之间的“鸡同鸭讲”。但我们在设计协议的时候,也不能只单纯考虑满足目前功能,还应该从更高的层次出发。原创 2023-11-13 08:48:53 · 147 阅读 · 0 评论 -
01 | 核心原理:能否画张图解释下RPC的通信流程?
我知道你肯定不喜欢听概念,我也是这样,看书的时候一看到概念就直接略过。不过,到后来,我才发现,“定义”是一件多么伟大的事情。当我们能够用一句话把一个东西给定义出来的时候,侧面也说明你已经彻底理解这事了,不仅知道它要解决什么问题,还要知道它的边界。所以,你可以先停下来想想,什么是 RPC。RPC 的全称是 Remote Procedure Call,即远程过程调用。原创 2023-11-12 23:31:06 · 54 阅读 · 0 评论 -
别老想着怎么用好RPC框架,得多花时间琢磨原理
把这些内容掌握后,就会发现,原来这些只是 RPC 的基础,RPC 还有更吸引人的点,它真正强大的地方是它的治理功能,比如连接管理、健康检测、负载均衡、优雅启停机、异常重试、业务分组以及熔断限流等等。例 2:我们经常会谈到的容器编排引擎 Kubernetes,它本身就是分布式的,Kubernetes 的 kube-apiserver 与整个分布式集群中的每个组件间的通讯,都是通过 gRPC 框架进行的。学习知识,解决问题,遇到新问题,继续学习,不断解决问题,最后会发现自己的学习曲线大概是这样的。原创 2023-11-12 23:28:13 · 63 阅读 · 0 评论