服务雪崩、降级与熔断

服务雪崩

假设存在如下调用链

而此时,Service A的流量波动很大,流量经常会突然性增加!那么在这种情况下,就算Service A能扛得住请求,Service BService C未必能扛得住这突发的请求。

此时,如果Service C因为抗不住请求,变得不可用。那么Service B的请求也会阻塞,慢慢耗尽Service B的线程资源,Service B就会变得不可用。紧接着,Service A也会不可用,这一过程如下图所示

如上图所示,一个服务失败,导致整条链路的服务都失败的情形,我们称之为服务雪崩。

那么,服务熔断和服务降级就可以视为解决服务雪崩的手段之一

服务熔断

服务熔断:当下游的服务因为某种原因变得突然不可用或响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用

需要说明的是熔断其实是一个框架级的处理,那么这套熔断机制的设计,基本上业内用的是断路器模式,如下图所示

  • 最开始处于closed状态,一旦检测到错误到达一定阈值,便转为open状态;
  • 这时候会有个 reset timeout,到了这个时间了,会转移到half open状态;
  • 尝试放行一部分请求到后端,一旦检测成功便回归到closed状态,即恢复服务;

业内目前流行的熔断器很多,例如阿里出的Sentinel,以及最多人使用的Hystrix

在Hystrix中,对应配置如下

1
2
3
4
5
6
# 滑动窗口大小,默认是20
circuitBreaker.requestVolumeThreshold
# 过多长时间,熔断器再次检测是否开启,默认为5000,即5s
circuitBreaker.sleepWindowInMilliseconds
# 错误率,默认50%
circuitBreaker.errorThresholdPercentage

每当20个请求中,有50%失败后,熔断器就会打开,此时再调用服务,将会直接返回失败,不再调远程服务。直到5s后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。

这些属于框架层级的实现,我们只要实现对应接口就好!

服务降级

下面有两种场景

  • 当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度
  • 当下游的服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回给用户
    其实要这么理解
  • 服务降级有很多种降级方式!如开关降级、限流降级、熔断降级
  • 服务熔断属于降级方式的一种

从实现上来说,熔断和降级必定是一起出现。因为当发生下游服务不可用的情况,这个时候为了对最终用户负责,就需要进入上游的降级逻辑了。因此,将熔断降级视为降级方式的一种
服务降级大多是属于一种业务级别的处理。当然,这里要讲的是另一种降级方式,也就是开关降级,这也是我们生产中常用的另一种降级方式
做法很简单,做个开关,然后将开关放在配置中心,在配置中心更改开关,决定哪些服务进行降级。
那么,在应用程序中部下开关的这个过程,业内也有一个名词,称为埋点!
那接下来最关键的一个问题,哪些业务需要埋点?一般有以下方法:

  1. 简化执行流程
    自己梳理出核心业务和非核心业务流程,然后在非核心业务流程加上开关,一旦发现系统扛不住,关掉开关,结束这些次要流程
  2. 关闭次要功能
    一个微服务下肯定有很多功能,那自己区分出主要功能和次要功能。然后次要功能加上开关,需要降级的时候,把次要功能关了
  3. 降级一致性
    假设,你在业务上发现执行流程没法简化了,愁啊!也没啥次要功能可以关了,桑心啊!那只能降低一致性了,即将核心业务流程的同步改异步,将强一致性改最终一致性!

总结

背景

分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务,如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应。

雪崩效应常见场景

  • 硬件故障:如服务器宕机,机房断电、光纤被挖断等
  • 流量激增:如异常流量,重试加大流量
  • 缓存穿透:一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端服务,造成服务提供者超负荷运行,引起服务不可用
  • 程序BUG:如程序逻辑导致内存泄漏,JVM长时间FullGC等
  • 同步等待:服务间采用同步调用模式,同步等待造成的资源耗尽

雪崩效应应对策略

针对造成雪崩效应的不同场景,可以使用不同的应对策略,没有一种通用所有场景的策略,参考如下:

  • 硬件故障:多机房容灾、异地多活等
  • 流量激增:服务自动扩容、流量控制(限流、关闭重试)等
  • 缓存穿透:缓存预加载、缓存异步加载等
  • 程序BUG:修改程序bug、及时释放资源等
  • 同步等待:资源隔离、MQ解耦、不可用服务调用快速失败等。资源隔离通常指不同服务调用采用不同的线程池;不可用服务调用快速失败一般通过熔断器模式结合超时机制实现

综上所述,如果一个应用不能对来自依赖的故障进行隔离,那该应用本身就处于被拖垮的风险中。因此,为了构建稳定、可靠的分布式系统,我们的服务应当具有自我保护意识,当依赖服务不可用时,当前服务启动自动保护功能,从而避免发生雪崩效应。

Author: Toyan
Link: https://toyan.top/service-avalanche/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
支付宝打赏
微信打赏