RPC系列七、熔断和限流

文章讨论了在分布式系统中,RPC如何应对高并发场景。服务端通过限流策略如令牌桶算法进行自我保护,防止过大的访问量导致服务不稳定。同时,介绍了应用和IP维度的限流配置,以及动态计算限流值的方法。此外,文章提到了调用端的自我保护机制——熔断,通过熔断器的关闭、打开和半打开状态切换,保护调用链路的稳定性。
摘要由CSDN通过智能技术生成

RPC 是解决分布式系统通信问题的一大利器,而分布式系统的一大特点就是高并发,所以说 RPC 也会面临高并发的场景。在这样的情况下,我们提供服务的每个服务节点就都可能由于访问量过大而引起一系列的问题,比如业务处理耗时过长、CPU 飘高、频繁 Full GC 以及服务进程直接宕机等等。但是在生产环境中,我们要保证服务的稳定性和高可用性,这时我们就需要业务进行自我保护,从而保证在高访问量、高并发的场景下,应用系统依然稳定,服务依然高可用。

一、服务端的自我保护

我们先看服务端,举个例子,假如我们要发布一个 RPC 服务,作为服务端接收调用端发送过来的请求,这时服务端的某个节点负载压力过高了,我们该如何保护这个节点?

在 RPC 调用中服务端的自我保护策略就是限流,那你有没有想过我们是如何实现限流的呢?

限流是一个比较通用的功能,可以在 RPC 框架中集成限流的功能,让使用方自己去配置限流阈值;我们还可以在服务端添加限流逻辑,当调用端发送请求过来时,服务端在执行业务逻辑之前先执行限流逻辑,如果发现访问量过大并且超出了限流的阈值,就让服务端直接抛回给调用端一个限流异常,否则就执行正常的业务逻辑。

限流方式有很多,比如最简单的计数器,还有可以做到平滑限流的滑动窗口、漏斗算法以及令牌桶算法等等。其中令牌桶算法最为常用。

我们可以假设下这样一个场景:发布了一个服务,提供给多个应用的调用方去调用,这时有一个应用的调用方发送过来的请求流量要比其它的应用大很多,这时我们就应该对这个应用下的调用端发送过来的请求流量进行限流。所以说我们在做限流的时候要考虑应用级别的维度,甚至是 IP 级别的维度,这样做不仅可以让我们对一个应用下的调用端发送过来的请求流量做限流,还可以对一个 IP 发送过来的请求流量做限流。

1、如何配置应用维度以及 IP 维度的限流?

RPC 框架真正强大的地方在于它的治理功能,而治理功能大多都需要依赖一个注册中心或者配置中心,我们可以通过 RPC 治理的管理端进行配置,再通过注册中心或者配置中心将限流阈值的配置下发到服务提供方的每个节点上,实现动态配置。

看到这儿,你有没有发现,在服务端实现限流,配置的限流阈值是作用在每个服务节点上的。比如说我配置的阈值是每秒 1000 次请求,那么就是指一台机器每秒处理 1000 次请求;如果我的服务集群拥有 10 个服务节点,那么我提供的服务限流阈值在最理想的情况下就是每秒 10000 次。

2、动态计算限流值

接着看这样一个场景:我提供了一个服务,而这个服务的业务逻辑依赖的是 MySQL 数据库,由于 MySQL 数据库的性能限制,我们是需要对其进行保护。假如在 MySQL 处理业务逻辑中,SQL 语句的能力是每秒 10000 次,那么我们提供的服务处理的访问量就不能超过每秒 10000 次,而我们的服务有 10 个节点,这时我们配置的限流阈值应该是每秒 1000 次。那如果之后因为某种需求我们对这个服务扩容了呢?扩容到 20 个节点,我们是不是就要把限流阈值调整到每秒 500 次呢?这样操作每次都要自己去计算,重新配置,显然太麻烦了。

可以让 RPC 框架自己去计算,当注册中心或配置中心将限流阈值配置下发的时候,我们可以将总服务节点数也下发给服务节点,之后由服务节点自己计算限流阈值

3、精准限流

动态计算限流值解决了一部分,还有一个问题存在,那就是在实际情况下,一个服务节点所接收到的访问量并不是绝对均匀的,比如有 20 个节点,而每个节点限流的阈值是 500,其中有的节点访问量已经达到阈值了,但有的节点可能在这一秒内的访问量是 450,这时调用端发送过来的总调用量还没有达到 10000 次,但可能也会被限流,这样是不是就不精确了?那有没有比较精确的限流方式呢?

上面提到的限流方式之所以不精确,是因为限流逻辑是服务集群下的每个节点独立去执行的,是一种单机的限流方式,而且每个服务节点所接收到的流量并不是绝对均匀的。

其实可以提供一个专门的限流服务,让每个节点都依赖一个限流服务,当请求流量打过来时,服务节点触发限流逻辑,调用这个限流服务来判断是否到达了限流阈值。甚至可以将限流逻辑放在调用端,调用端在发出请求时先触发限流逻辑,调用限流服务,如果请求量已经到达了限流阈值,请求都不需要发出去,直接返回给动态代理一个限流异常即可。

这种限流方式可以让整个服务集群的限流变得更加精确,但也由于依赖了一个限流服务,它在性能和耗时上与单机的限流方式相比是有很大劣势的。至于要选择哪种限流方式,就要结合具体的应用场景进行选择了。

二、调用端的自我保护

举个例子,假如我要发布一个服务 B,而服务 B 又依赖服务 C,当一个服务 A 来调用服务 B 时,服务 B 的业务逻辑调用服务 C,而这时服务 C 响应超时了,由于服务 B 依赖服务 C,C 超时直接导致 B 的业务逻辑一直等待,而这个时候服务 A 在频繁地调用服务 B,服务 B 就可能会因为堆积大量的请求而导致服务宕机。

 由此可见,服务 B 调用服务 C,服务 C 执行业务逻辑出现异常时,会影响到服务 B,甚至可能会引起服务 B 宕机。这还只是 A->B->C 的情况,试想一下 A->B->C->D->……呢?在整个调用链中,只要中间有一个服务出现问题,都可能会引起上游的所有服务出现一系列的问题,甚至会引起整个调用链的服务都宕机,这是非常恐怖的。

所以说,在一个服务作为调用端调用另外一个服务时,为了防止被调用的服务出现问题而影响到作为调用端的这个服务,这个服务也需要进行自我保护。

 1、熔断

熔断器的工作机制主要是关闭、打开和半打开这三个状态之间的切换。

在正常情况下,熔断器是关闭的;当调用端调用下游服务出现异常时,熔断器会收集异常指标信息进行计算,当达到熔断条件时熔断器打开,这时调用端再发起请求是会直接被熔断器拦截,并快速地执行失败逻辑;

当熔断器打开一段时间后,会转为半打开状态,这时熔断器允许调用端发送一个请求给服务端,如果这次请求能够正常地得到服务端的响应,则将状态置为关闭状态,否则设置为打开。

2、RPC 框架中,我们该如何整合熔断器呢?

熔断机制主要是保护调用端,调用端在发出请求的时候会先经过熔断器。我们可以回想下 RPC 的调用流程:

建议在动态代理阶段整合熔断,因为在 RPC 调用的流程中,动态代理是 RPC 调用的第一个关口。在发出请求时先经过熔断器,如果状态是闭合则正常发出请求,如果状态是打开则执行熔断器的失败策略。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值