微服务之熔断、降级、限流

1、引言

在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC)。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这种现象就是“服务雪崩”。

如上图图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。

在复杂的分布式架构中,应用程序会有很多的依赖,这些依赖不可避免地在某些时候可能会失败。高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险。

很多服务框架会提供服务重试机制,在很大程度上解决了由于网络瞬时不可达的问题。但是在很多的情况下造成服务雪崩的元凶正是服务重试机制

某个服务本来就已经出现问题了,造成资源占用无法释放、请求延时等问题。这时在请求失败之后又不断的发送重试请求,在原本就无法释放的资源基础上继续膨胀式占用,导致整个系统资源耗尽。导致服务雪崩。

要防止系统发生雪崩,就必须要有容错设计。如果遇到突增流量,一般的做法是对非核心业务功能采用熔断降级的措施来保护核心业务功能正常服务,而对于核心功能服务,则需要采用限流的措施。

2、熔断

熔断来自英文短语circuit breakercircuit breaker在电工学里就是断路器的意思。它就相当于一个开关,打开后可以阻止流量通过。比如保险丝,当电流过大时,就会熔断,从而避免元器件损坏。

服务熔断是指调用方访问服务时通过断路器做代理进行访问,断路器会持续观察服务返回的成功、失败的状态,当失败超过设置的阈值时断路器打开,请求就不能真正地访问到服务了。

一般而言,熔断状态不会一直持续,而是有一个时间范围,时间过了以后再去尝试请求服务提供者,一旦服务提供者的服务能力恢复,请求将继续可以调用服务提供者。

熔断有3 种状态:CLOSED(关闭)、OPEN(开启)、HALF OPEN(半开启);断路器默认处于关闭状态。

  • 断路器默认处于“关闭”状态,当服务提供者的请求失败比例到达阈值,就会触发断路器“开启”;
  • 断路器开启后进入熔断时间,到达熔断时间终点后重置熔断时间,进入“半开启”状态;
  • 在半开启状态下,如果服务提供者的服务能力恢复,则断路器关闭熔断状态,进而进入正常的服务状态;
  • 在半开启状态下,如果服务提供者的服务能力未能恢复,则断路器再次触发服务熔断,进入熔断时间;

3、降级

服务熔断通常适与服务降级配合使用。在服务发生熔断后,一般会让请求走事先配置的处理方法,这个处理方法就是一个降级逻辑。

简言之,服务降级是一种兜底的服务策略,体现了一种“实在不行就怎么样”的思想。想去北京买不到飞机票,实在不行就坐高铁去吧;感冒了想去看病挂不上号,实在不行就先回家吃点药睡一觉吧;实在不行之后的处理方法,被称为fallback方法

当服务提供者故障触发调用者服务的熔断机制,服务调用者就不再调用远程服务方法,而是调用本地的fallback方法。此时你需要预先提供一个处理方法,作为服务降级之后的执行方法,fallback返回值一般是设置的默认值或者来自缓存。

除了可以在服务调用端实现服务降级,还可以在服务提供端实现服务降级。

 

实际上在大型的微服务系统中,服务提供者和服务消费者并没有严格的区分,很多的服务既是提供者,也是消费者。

4、限流

4.1 为什么需要限流?

举一个我们生活中的例子:一些热门的旅游景点,往往会对每日的旅游参观人数有严格的限制,比如厦门的鼓浪屿、北京的故宫等,每天只会卖出固定数目的门票,如果去的晚了,可能当天的票就已经卖完了,当天就无法进去游玩了。

为什么旅游景点要做这样的限制呢?多卖一些门票多赚一些钱岂不是更好?

原因在于景点的服务资源是有限的,每日能服务的人数是有限的,一旦放开限制了,景点的工作人员就会不够用,卫生情况也得不到保障,安全也有隐患,超密集的人群也会严重的影响游客的体验。 但由于景区名气大,来游玩的旅客络绎不绝,远超出了景区的承载能力,因此景区只好做出限制每日人员流量的举措。

同理,在IT软件行业中,系统服务也是这样的。

服务限流是指当系统资源不够,不足以应对大量请求,即系统资源与访问量出现矛盾的时候,我们为了保证有限的资源能够正常服务,因此对系统按照预设的规则进行流量限制或功能限制的一种方法。

如果某系统单位时间内可服务100W用户,但是却突然来了300W用户,由于用户流量的随机性,如果不加以限流,很有可能这300W用户一下子就压垮了系统,导致所有人都得不到服务。

因此为了保证系统至少还能为100W用户提供正常服务,需要对系统进行限流设计。

有的人可能会想,既然会有300W用户来访问,那为啥系统不干脆设计成能足以支撑这么大量用户的集群呢?

这是个好问题。如果系统是长期有300W的用户来访问,肯定是要做上述升级的,但是常常面临的情况是,系统的日常访问量就是100W,只不过偶尔有一些不可预知的特定原因导致的短时间的流量激增,这个时候,公司往往出于节约成本的考虑,不会为了一个不常见的尖峰来把我们的系统扩容到最大的尺寸。

4.2 什么是限流

限流定义:

限制到达系统的并发请求数量,保证系统能够正常响应部分用户请求,而对于超过限制的流量,则通过拒绝服务的方式保证整体系统的可用性。

常见限流方式:

基于请求限流:指从外部请求的角度考虑限流。

基于资源限流:指从系统内部考虑,找到影响性能的关键资源,对其使用上限限制。

另外,我们需要了解几个概念:TPS、HPS、QPS;

TPS:系统吞吐量,是衡量系统性能的关键指标,按照事务的完成数量来限流是最合理的。
HPS:每秒请求数,指每秒钟服务端收到客户端的请求数量。
QPS:服务端每秒能够响应的客户端查询请求数量。

目前主流的限流方法多采用 HPS 作为限流指标,下面介绍几种常见的服务端限流算法。

4.3 计数器限流

最简单的实现方式,维护一个计数器,来一个请求计数加一,达到阈值时,直接拒绝请求。比如限流qps为100,算法的实现思路就是从第一个请求进来开始计时,在接下去的1s内,每来一个请求,就把计数加1,如果累加的数字达到了100,那么后续的请求就会被全部拒绝。等到1s结束后,把计数恢复成0,重新开始计数。

但是这个方法存在 2 个明显的问题。

1)单位时间(比如 1s )很难把控,如下图:

这张图上,从下面时间看, HPS 没有超过 100 ,但是从上面看 HPS 超过 100 了。

2)突刺现象

如果在单位时间1s内的前10ms,已经通过了100个请求,那后面的990ms,只能眼巴巴的把请求拒绝,我们把这种现象称为“突刺现象

为了解决这两个缺陷,就有了下面要介绍的基于滑动时间窗口的算法。

4.4 滑动时间窗口

滑动时间窗口算法是目前比较流行的限流算法,主要思想是把时间看做是一个向前滚动的窗口,如下图:

开始的时候,我们把 t1~t5 看做一个时间窗口,每个窗口 1s ,如果我们定的限流目标是每秒 50 个请求,那 t1~t5 这个窗口的请求总和不能超过 250 个。

这个窗口是滑动的,下一秒的窗口成了 t2~t6 ,这时把 t1 时间片的统计抛弃,加入 t6 时间片进行统计。这段时间内的请求数量也不能超过 250 个。

滑动时间窗口的优点是解决了流量计数器算法的缺陷,但是也有 2 个问题:

  • 流量超过就必须抛弃或者走降级逻辑;
  • 对流量控制不够精细,不能限制集中在短时间内的流量,也不能削峰填谷;

4.5 漏桶算法

为了消除"突刺现象",可以采用漏桶算法实现限流,漏桶算法这个名字就很形象,算法内部有一个容器,类似生活用到的漏斗,当请求进来时,相当于水倒入漏斗,然后从下端小口慢慢匀速的流出。不管上面流量多大,下面流出的速度始终保持不变。

不管服务调用方多么不稳定,通过漏桶算法进行限流,每10毫秒处理一次请求。因为处理的速度是固定的,请求进来的速度是未知的,可能突然进来很多请求,没来得及处理的请求就先放在桶里,既然是个桶,肯定是有容量上限,如果桶满了,那么新进来的请求就丢弃。

在算法实现方面,可以准备一个队列,用来保存请求,另外通过一个线程池来定期从队列中获取请求并执行,可以一次性获取多个并发执行。

漏桶算法的优点是实现简单,可以使用消息队列来削峰填谷,但是也有3个问题需要考虑:

  • 漏桶的大小,如果太大,可能给服务端带来较大处理压力,太小可能会有大量请求被丢弃;
  • 使用缓存请求的方式,会使请求响应时间变长。
  • 漏桶给服务端的请求发送速率均匀,面对流量徒增时调整起来不方便。

4.6 令牌桶算法

从某种意义上讲,令牌桶算法是对漏桶算法的一种改进,漏桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用

在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。

放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

 令牌桶算法实现并不复杂,使用信号量就可以实现。在实际限流场景中使用最多,比如 google 的 guava 中就实现了令牌桶算法限流,感兴趣可以研究一下。

4.7 分布式限流

前面讨论的几种算法都属于单机限流的范畴,如果在分布式系统场景下,上面介绍几种限流算法是否还适用呢?

以令牌桶算法为例,假如在电商系统中客户下了一笔订单,如下图:

 比如为了限制某个资源被每个用户或者商户的访问次数,5s只能访问2次,或者一天只能调用1000次,这种需求,单机限流是无法实现的,这时就需要通过集群限流进行实现。

如果我们把令牌桶单独保存在一个地方(比如 Redis 中)供整个分布式系统用,那客户端在调用组合服务,组合服务调用订单、库存和账户服务都需要跟令牌桶交互,交互次数明显增加了很多。

有一种改进就是客户端调用组合服务之前首先获取四个令牌,调用组合服务时减去一个令牌并且传递给组合服务三个令牌,组合服务调用下面三个服务时依次消耗一个令牌。

5、总结

限流、熔断服务降级是系统容错的重要设计模式,从一定意义上讲限流和熔断也是一种服务降级的手段。熔断服务降级主要是针对非核心业务功能,而核心业务如果流程超过预估的峰值,就需要进行限流。对于限流,选择合理的限流算法很重要,令牌桶算法优势很明显,也是使用最多的限流算法。在系统设计的时候,这些模式需要配合业务量的预估、性能测试的数据进行相应阈值的配置,而这些阈值最好保存在配置中心,方便实时修改。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值