当前互联网处理的业务场景都极为复杂,各大公司都会根据自己的业务场景搭建微服务来保证单个服务只处理一块业务,这样做能极大的提升开发效率,满足快速迭代的需要,但带来的问题却是多个服务下会导致整体服务的可用性下降。
互联网服务的可用性一般用 SLA(Service Level Agreement 可以翻译为服务水平协议)来表示,而我们通常所说的 N 个 9 就是对高可用服务的一个衡量指标。9 越多代表全年服务可用时间越长,服务会更可靠。
现今互联网架构里保证服务的高可用和高稳定性的时候,无非就是熔断、降级、限流、隔离这几个策略。今天主要从以下几个方面来聊聊熔断降级这块内容。
服务整体的调用关系如下图:
服务流量监控图,如下图:
服务请求耗时,如下图:
本地服务 B 接收上游服务 A 的请求,并请求到下游服务 C 进行处理后返回给服务 A。
说下上边两张图中的几个要素:
上图示服务的调用异常使得本地服务 B 短时间的阻塞,进而导致了本地服务吞吐量急剧下降,严重影响服务的可用性和稳定性。经过上述问题之后,我们决定对服务里边的依赖进行熔断降级处理,其目的主要是为了保护自己的系统。
比如我们依赖的底层服务出现不稳定时,请求的响应时间会变长,调用服务的响应时间也会变长,短时间内的问题可能会导致自身服务的吐吞量降低,即单位时间处理的请求数降低(见上述流量监控图)。严重时会产生线程堆积,极端情况下会耗尽自身应用的线程,从而导致服务不可用。
需要强调的是,我们做熔断降级主要是为了保护本地服务的可用性,熔断降级都是在客户端来做的。因为当前的微服务都是分布式的,调用链路非常复杂。当某个单层调用出现问题时,就会产生放大效果,就可能层层级联,最终导致整个链路不可用。降级就是对暂定不稳定的依赖进行切断状态,即暂时不去进行调用。
熔断降级在实际过程中是 2 个步骤:
降级逻辑需要与当前处理场景相结合,并没有统一的结论。
当前业界比较流行的降级组件大概有 Sentinel、Hystrix、Resilience4j,三者区别主要见下:
这里再多说几句:
Sentinel 属于 Alibaba 公司的开源产品,代码可读性相对 Hystrix 友好很多(这个只是个人看法)。而且最重要的是 Sentinel 能支持对于不同异常的处理,比如能区分出正常的业务异常还是需要熔断的异常。使用者可以选择在应用层对异常进行处理,比如直接抛出,这样业务层可以根据不同的异常选择不同的处理逻辑,比如重试等策略。
在隔离策略看来,线程池的隔离策略无疑是优秀的,其原理就是对不同的接口设置不同的线程池,这样就能与其他接口使用的资源做到基本的隔离,而且能支持异步。但是线程池隔离策略最大的问题就是线程的运行和上下文切换会增加服务器的负载。而信号量隔离策略其原理可以理解为给接口设置了一个计数器,只能支持这么多请求的调用,超出该阈值则会触发 fallback,不能进行异步调用,对服务器负载影响相对较小。
Sentinel 降级的原理其实并不复杂,熔断降级原理可以拆解为降级规则定义,降级数据统计,降级规则验证,降级实施几个方面去理解。
其中降级规则的定义包含降级策略,降级参数两个方面,降级数据统计指的是在指定降级策略下边对请求或者异常进行计数,降级规则验证指的是在判断是否触发降级,降级实施指的是在熔断后所采用的降级方案。下来将对这几块的内容进行详述。
Sentinel 的降级策略见下图:
需要注意的是,在慢调用策略中,你的请求超过了指定 RT,此时该请求还会继续往下执行的,并不会中断正在执行的请求,只是会在统计时将超出阈值的请求按慢请求计数。
这块的 grade 指的是上面描述的三种策略,statIntervalMs 表示的是数据统计时长,timeWindow 表示的是熔断的时长。触发熔断的条件是总请求打到阈值并且请求数必须大于 minRequestAmount(该值默认是 5)。
先看段 Sentinel 降级的一段代码,展示如下:
如图示代码,在 Sentinel 里面,所有的资源都对应一个资源名称(resourceName),每次资源调用都会创建一个 Entry 对象。每个请求过来都会进入 SphU.entry("自定义资源名") 逻辑。当在 entry("自定义资源名") 逻辑里达到了降级的阈值,则会抛出 BlockException 进行熔断而触发降级。
再来看看 entry("自定义资源名") 里边是如何降级的,要触发降级的前提是要达到规定的阈值,判断是否达到阈值前提是要进行请求数据统计。Entry 创建的时候,同时也会创建一系列功能插槽(slot chain),这些插槽有不同的职责。
下图摘自官网:
这些 Slot 的加载是基于 SPI 机制加载的,加载的顺序是每个 Slot 上边有个注解,来表示加载的顺序。
请求计数的入口在 StatisticSlot 类,负责统计资源的实时状态,调用到 slotchain 中的任意一个 slot 时,都会触发该 slot 的 entry 方法。
这块代码的逻辑比较复杂,大概逻辑为:
关于数据统计,主要会牵扯到 ArrayMetric、BucketLeapArray、MetricBucket、WindowWrap 等类。
降级规则验证的入口在 DegradeSlot 类,代码如下:
在验证降级规则时,通过 resourceName 取到初始化时加载到内存的降级规则 CircuitBreaker,然后判断是否通过,没通过时直接抛出 DegradeException。
判断当前请求是否通过的代码如下:
这块主要是根据 State 的变化来决定是降级还是正常,State 的变化依赖于上边数据的统计的结果。
降级实施这块其实有两块内容:
熔断降级整个过程中状态的流转如下图:
熔断后降级的业务处理,简单理解为降级后你要执行的方法,也可以快速返回 null。这块非常要注意的是,你的业务处理一定要考虑你所有的业务场景,比如返回 null 值对上层所有调用方的影响是什么,一定要搞清楚这个再写降级逻辑。
Sentinel 的功能非常强大,本文只是针对熔断降级这块做了大概的介绍。降级的原理需要结合降级参数在不同的降级策略下的表现,每次请求都会进入当前 entry()中进行数据统计和降级等操作,需要结合上边 State 状态变化的流程来一块理解。其实熔断的时候需要基于滑动时间窗口进行数据统计,这块内容也比较核心,希望改天能单开一篇再给大家详细进行讲解。
来源:
https://www.toutiao.com/article/7008891741885907494/?log_from=90fae59166c7f_1653359151083
“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com
来都来了,走啥走,留个言呗~
IT大咖说 | 关于版权
由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!
感谢您对IT大咖说的热心支持!