前言
最近在看有关分布式和微服务的知识,首先第一个碰到的就是RPC框架,常见的RPC框架其实有很多,比较常见的比如:阿里的Dubbo、ApacheThrift、谷歌的gRPC、腾讯的tRPC等等。RPC作为远程调用协议在微服务架构中可以说是比较常见了,而在学习整个微服务组件生态之前,我们还是需要学习一些理论知识。下面的这篇文章荔枝主要梳理有关RPC的基础知识,希望这篇文章能对有需要的小伙伴有帮助~~~
文章目录
前言
一、RPC
1.1 为什么要使用RPC框架
1.2 RPC调用过程
1.3 负载均衡
1.4 熔断、限流和服务降级
1.4.1 熔断
1.4.2 限流
1.4.3 服务降级
1.5 优雅启动
二、时间轮
总结
一、RPC
RPC(Remote Procedure Call,远程过程调用)一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。既然是协议就一定会有具体规范实现,因此开发了RPC框架,RPC框架隐藏了底层的通讯细节,让开发者像调用本地函数一样去调用远程函数而不用关心具体底层实现。为实现该目标,RPC 框架提供了一种透明调用机制,让使用者不必显式的区分本地调用和远程调用。
常见的RPC框架
- Dubbo:Dubbo是一个分布式服务框架,以及SOA治理方案,是国内最早开源的 RPC 框架,。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,Dubbo自2011年开源后,已被许多非阿里系公司使用。
- Motan:微博内部使用的 RPC 框架,于 2016 年对外开源,仅支持 Java 语言。
- Tars:腾讯内部使用的 RPC 框架,于 2017 年对外开源,仅支持 C++ 语言。
- Spring Cloud:国外 Pivotal 公司 2014 年对外开源的 RPC 框架,提供了丰富的生态组件。
- gRPC:Google 于 2015 年对外开源的跨语言 RPC 框架,支持多种语言。
- Thrift:最初是由 Facebook 开发的内部系统跨语言的 RPC 框架,2007 年贡献给了 Apache 基金,成为Apache 开源项目之一,支持多种语言。
1.1 为什么要使用RPC框架
上面的介绍中我们了解到,在分布式的场景下,RPC就是A服务去远程调用B服务接口的方法封装。随着并发需求越来越大,软件架构升级过程中我们将不同的服务功能进行了解耦,但不同服务之间仍然还是存在逻辑关系的,比如电商系统中的会员服务和订单服务,我们需要对一些公共的业务逻辑抽象出来,由于这些服务又可能是运行在不同的集群节点中,服务之间的调用我们需要一种方便调用开发的方式或者说是范式,因此选择了RPC。
RPC特点
- RPC框架一般使用长链接,不必每次通信都要3次握手,减少网络开销。
- RPC框架一般都有注册中心,有丰富的监控管理。发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作协议私密,安全性较高
- RPC 协议更简单内容更小,效率更高,服务化架构、服务化治理,RPC框架是一个强力的支撑。
1.2 RPC调用过程
RPC的调用过程最重要的部分就是序列化和反序列化的操作,在网络传输中,数据必须采用二进制形式, 所以在RPC调用过程中, 需要采用序列化技术,对入参对象和返回值对象进行序列化与反序列化。
在真实的分布式场景下,系统一般是以集群的方式提供服务的,也就是说一个接口往往会有很多的服务提供者,因此RPC需要一种机制来选取一个用于处理请求的节点——路由。那么如何实现路由机制呢?我们主要通过RPC路由策略来实现:接受请求 > 请求校验 > 路由策略 > 负载均衡,这种路由策略一般会将部分请求调用到新上线的实例,相对于传统的灰度发布方式中定点试错来选择发布更多或者回滚的方式减少了很多的试错成本。
常见的路由策略有IP路由策略,但在某些场景下可能还需要更细粒度的路由方式,比如更具SessionID来保证会话的有效性,因此我们需要参数化路由来支持更细粒度的灰度发布。
1.3 负载均衡
RPC 的负载均衡是由 RPC 框架自身提供实现,RPC 的服务调用方会与“注册中心”下发的所有服务节点建立长连接,在每次发起 RPC 调用时,服务调用方都会通过配置的负载均衡插件,自主选择一个最佳的服务节点,发起 RPC 调用请求。
负载均衡策略:轮询、随机(Dubbo)、权重、最少连接
自适应负载均衡:RPC 的负载均衡完全由 RPC 框架自身实现,通过所配置的负载均衡组件,自主选择合适服务节点,也就是能够让调用方自主根据每个服务节点处理请求的能力判断分配相应的流量。实现自适应负载均衡需要服务调用方收集与之建立长连接的每个服务节点的指标(CPU核数、内存、请求处理耗时指标、节点状态),依据一定的算法策略来调度。
1.4 熔断、限流和服务降级
熔断限流机制
在高并发的场景下,面对猛增的流量,集群中的服务节点可能会因为访问量过大而引发一系列的问题,为了保证业务系统在高并发、高访问量的场景下依旧可以保证高可用,服务端需要具备一定的自我保护能力。
1.4.1 熔断
熔断是在流量过大导致服务不可用的时候,自动断开与服务调用方之间的交互,并通过自我诊断来尝试修复。熔断可以防止应用程序不断地尝试可能超时和失败的服务,使应用程序更换服务执行而不必等待下游服务修正错误服务。避免对故障或超时服务的无效调用,从而避免了系统的连锁反应和雪崩效应。
熔断器
熔断器模式最重要的是能让应用程序自我诊断下游系统的错误是否已经修正,如果没有,不放量去请求,如果请求成功了,慢慢的增加请求,再次尝试调用。RPC熔断器有三种状态:全开、半开、关闭。
CLOSED
:默认状态。断路器观察到请求失败比例没有达到阈值,断路器认为被代理服务状态良好。OPEN
:断路器观察到请求失败比例已经达到阈值,断路器认为被代理服务故障,打开开关,请求不再到达被代理的服务,而是快速失败。HALF OPEN
:断路器打开后,为了能自动恢复对被代理服务的访问,会切换到半开放状态,去尝试请求被代理服务以查看服务是否已经故障恢复。如果成功,会转成CLOSED
状态,否则转到OPEN
状态。
1.4.2 限流
限流是一种预防机制,是对系统的被请求效率以及内部的部分功能的执行效率加以限制,防止因突发的流量激增导致系统不可用。
限流算法
在学习限流算法之前我们需要明确几个限流指标:
- TPS:系统吞吐量是衡量系统性能的关键指标,按照事务的完成数量来限流理论上最合理。但按照事务来限流其实并不现实,在分布式系统中完成一笔事务往往需要多个系统的配合。如果按照
TPS
来进行限流,时间粒度可能会很大大,很难准确评估系统的响应性能; - HPS:每秒请求数,指每秒钟服务端收到客户端的请求数量;
- QPS:服务端每秒能够响应的客户端查询请求数量;
❝如果一个请求完成一笔事务,那
TPS
和HPS
是等同的。但在分布式场景下,完成一笔事务可能需要多次请求,所以TPS
和HPS
指标不能等同看待。 ❞
常见的限流的算法:计数器、平滑限流的滑动窗口、令牌桶算法、漏斗算法、漏桶算法
计数器算法
在一定的时间间隔内指定能够处理的最大请求数量,多余的请求会被丢弃。
缺点:虽然理论比较简单,但是时间的尺度比较难以把控。比如在5s时间中,按照单位时间1s服务收到的请求分别为60,60,40,50,40。如果我们的时间尺度选择的是2s,肯定会丢掉一定数量的请求,但如果设置的是5s,那就不需要丢弃数据。
滑动窗口
滑动窗口是在计数器算法上提出的一种改进的想法,也就是我们把一段时间上达到分布式系统上的请求类比为一个场景:一个滚动窗口以一定的时间尺度和一定的请求限制在一条时间轴上滑动,通过该算法可以在每前进一个时间的尺度中。
优点:有效缓解了计数器算法的缺点
缺点:对于时间粒度上的把控还不够精细,不能限制短时间内激增的流量,也不能削峰填谷。
漏桶算法
前面的两种限流算法并不能削峰填谷,也就是说如果在流量足够大的时候只能选择丢弃或者降级处理,在流量较少的时候也不能比较好的利用起系统的资源。漏桶算法通过维护一个消息队列来实削峰填谷。也就是通过缓存请求的方式,在流量高峰期将请求放进消息队列中,直到队列满了才开始丢弃或降级处理。由于漏桶算法是以设定速率来向服务端发送请求的
但是这也会带来几个问题:如何设置消息队列的队列大小?漏斗给服务端的请求发送速率应该设置为多少?缓存请求带来的响应时间过长是否符合业务场景。很明显前两个问题答案一定是通过测试来得到的。
令牌桶算法
令牌桶算法以设定速率生成令牌并放入令牌桶中,通过分发令牌来限制客户端发送请求的权力,如果客户端拿不到令牌就只能被拒绝或者降级处理。令牌桶算法解决了漏桶算法的问题,而且实现并不复杂,使用信号量就可以实现。在实际限流场景中使用最多。但在分布式场景下,令牌桶可能还需要改进,为了降低客户端在调用服务的时候与令牌桶的交互次数,以提高系统性能,可以设置客户端在访问一个系统下的N服务可以同时获得N个令牌,调用一个服务就消耗一个令牌。
需要注意的是:令牌桶的容量和令牌生成速率是固定的,但是放行的速度不是固定的,只要桶中还有剩余令牌,客户端一有请求过来就能申请成功,然后放行。
hystrix限流
hystrix可以使用信号量和线程池来进行限流。
参考文章:
10张图带你彻底搞懂限流、熔断、服务降级-腾讯云开发者社区-腾讯云 (tencent.com)
今天一定要搞清楚限流、熔断和降级 - 掘金
https://www.cnblogs.com/whgk/p/14371290.html#%E5%BC%82%E6%AD%A5%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6
1.4.3 服务降级
服务降级在电商的秒杀场景下比较常见,对于短时间的流量激增,如果系统服务节点无法容纳处理,我们就需要短时间终止次要服务来降低系统面临的流量压力。降级一般考虑的是分布式系统的整体性,从源头上切断流量的来源。降级更像是预估手段,在预计流量峰值前提下,提前通过配置功能降低服务体验,或暂停次要功能,保证系统主要流程功能平稳响应。
降级分类
- 超时降级 —— 主要配置好超时时间和超时重试次数和机制,并使用异步机制探测恢复情况
- 失败次数降级 —— 主要是一些不稳定的API,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
- 故障降级 —— 如要调用的远程服务挂掉了(网络故障、DNS故障、HTTP服务返回错误的状态码和RPC服务抛出异常),则可以直接降级
- 限流降级 —— 当触发了限流超额时,可以使用暂时屏蔽的方式来进行短暂的屏蔽
对应的三种降级策略
- 直接拒绝:当系统负载过高,对于不紧急不重要的请求,可以直接返回错误码或错误信息,拒绝服务。
- 排队等待:对于一些需要处理但不是很紧急的请求,可以将其放入请求队列中,等待系统空闲时再进行处理。这种方式适用于系统负载已经很高,但请求依然需要处理的情况。
- 返回缺省值:对于一些可以使用缺省值代替的请求,可以直接返回缺省值,减少系统处理的压力。这种方式适用于系统出现一些临时性的故障,无法提供完整的服务时。
1.5 优雅启动
启动预热就是让刚启动的服务,不直接承担全部的流量,而是让它随着时间的移动慢慢增加调用次数,最终让流量缓和运行一段时间后达到正常水平。
获取服务提供方的启动时间的方法:
- 服务提供方在启动的时候,主动将启动的时间发送给注册中心;
- 注册中心来检测, 将服务提供方的请求注册时间作为启动时间。
实现:调用方通过服务发现获取服务提供方的启动时间, 然后进行降权,减少被负载均衡选择的概率,从而实现预热过程。
在Dubbo中也引入了warmup特性来实现热启动。
二、时间轮
在Dubbo中,为增强系统的容错能力,会有相应的监听判断处理机制。比如RPC调用的超时机制的实现,消费者判断RPC调用是否超时,如果超时会将超时结果返回在最开始的实现中,通过轮询机制来扫描逐个判断是否请求超时,这就会带来很多的无意义的循环检测判断操作,因此Dubbo采用了时间轮算法来优化。
原理
在时钟轮机制中,有时间槽和时钟轮的概念,时间槽就相当于时钟的刻度;而时钟轮就相当于指针跳动的一个周期,我们可以将每个任务放到对应的时间槽位上。 时间轮算法的本质就是减少轮询产生的额外扫描操作
应用:
- 调用超时: 客户端调用超时的处理就可以应用到时钟轮,我们每发一次请求,都创建一个处理请求超时的定时任务放到时钟轮里,在高并发、高访问量的情况下,时钟轮每次只轮询一个时间槽位中的任务,这样会节省大量的 CPU。
- 启动加载: 调用端与服务端启动也可以应用到时钟轮,比如说在服务启动完成之后要去加载缓存,执行定时任务等, 都可以放在时钟轮里
- 定时心跳检测: RPC 框架调用端定时向服务端发送的心跳检测,来维护连接状态,我们可以将心跳的逻辑封装为一个心跳任务,放到时钟轮里。心跳是要定时重复执行的,而时钟轮中的任务执行一遍就被移除了,依次我们需要在执行结束就要重设这个任务的执行时间,把它重新丢回到时钟轮里循环进行心跳检测。
降级其实也包含了熔断和限流,二者可以看成是服务降级过程中的手段之一。
总结
总结这篇文章还是要感谢几位博主的文章,真的学到了很多!尤其是腾讯云社区的文章质量真的很高哈哈哈哈,至少对于荔枝粗浅的知识储备是这样的,继续加油!!!
今朝已然成为过去,明日依然向往未来!我是荔枝,在技术成长之路上与您相伴~~~
如果博文对您有帮助的话,可以给荔枝一键三连嘿,您的支持和鼓励是荔枝最大的动力!
如果博文内容有误,也欢迎各位大佬在下方评论区批评指正!!!