什么是服务雪崩?
服务雪崩效应是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。
我来用一个模拟场景带你感受一下服务雪崩的厉害之处。假设我有一个微服务系统,服务提供者不可用,比如,服务提供者A服务的访问压力过大,或者是网络原因,硬件原因等等多种因素,造成了服务提供者的不可访问。此时,相应的服务调用者B服务,就无法成功调用其提供的接口,并且造成线程阻塞,挤压线程。随着调用次数的增多,挤压的线程越来越多,那么这个服务调用者的抗并发量,就越来越少,直至最后崩掉。依次类推,B服务还为C服务提供了接口,那么B服务崩掉了,C服务的线程也开始挤压,直至C服务崩掉。依次类推,最后的效果就是所有的微服务都崩掉了。这就是服务雪崩效应。
Sentinel 服务容错的思路
Sentinel 是 Spring Cloud Alibaba 的一款服务容错组件,我们也经常把它叫做“防流量哨兵”。它是阿里巴巴双十一大促核心场景的保护神,内置了丰富的服务容错应用场景。它以流量作为切入点,通过各种内外防控手段达到维持服务稳定性的目的。
阿里系的 Sentinel 解决服务稳定性的思路是什么呢?就拿服务雪崩这种稳定性崩坏的场景来说,这种极端场景的发生有两个主要因素,一个是外部的高并发流量导致的请求数量增多,超过了集群的吞吐量,另一个是内部各种未知异常导致的接口响应异常超时。
我们可以采用“内外兼修”的思路来摆平服务雪崩的两个主要因素。所谓“内”,是指内部的异常治理,所谓“外”,则是外部用户流量的疏导。内外兼修的根本目的,是从内部和外部双管齐下对集群访问量进行减压,下面我们深入分析一下 Sentinel 是如何通过内外兼修的手段做服务容错的。
内部异常治理
在 Sentinel 中,我们可以采用降级和熔断的方式处理内部的异常。所谓降级,是指当服务调用发生了响应超时、服务异常等情况时,我们在服务内部可以执行一段“降级逻辑”。
在降级逻辑中,你可以选择静默处理,即忽略掉异常继续执行后续逻辑;或者你可以返回一个让业务可以继续执行下去的默认结果;又或者,在降级逻辑中尝试重试、或者恢复异常服务。从这里我们可以看出,降级是针对“单次服务调用异常”而执行的处理逻辑。
而所谓熔断,对已经挂掉的服务,直接不再进行调用,而是直接返回结果,这就是熔断。以此避免已经挂掉的服务对调用者造成的影响。
我们可以看出,当服务进入到了“熔断状态”以后,当前服务对下游目标服务的调用行为也就停止了,这样一来就大大降低了下游服务的访问压力。
关于降级熔断,我的经验是主链路服务(也就是核心业务链路的重要服务)一定要设置降级预案,防止服务雪崩在核心业务上的传播。除此之外,对于非核心链路的服务,应该设置手动降级开关,在大促等高并发场景下做主动降级,将额外的计算资源通过弹性方案匀给主链路服务。
关于降级的判定条件,需要结合全链路压测的结果来判定。通常我们在双 11 这类大促场景下,会组建一个稳定性团队专门负责全链路压测,并根据多轮次的压测结果来调整各个服务集群的水位,并对降级和限流判定条件进行微调,以期达到最佳的主链路吞吐量。
外部流量控制
提到外部流量控制,你一定会想到“限流”。没错,限流是流量整形 / 流控方案的一种。在 Sentinel 中我们可以根据集群的处理能力,为每个服务设置一个限流规则,从 QPS 维度或者并发线程数的维度控制外部的访问流量。一旦访问量超过阈值,后续的请求就会被“fast fail”,这是最为常用的一种限流手段。但 Sentinel 所支持的方案可不止这一种。
从流量整形的效果来看,除了“快速失败”以外,我们还可以在 Sentinel 中选择预热模型和排队模型。顾名思义,预热模型就是在一段规定的预热时间窗口内,由低到高逐渐拉高流量阈值,直到达到预设的最高阈值为止。
而对于排队模型来说,如果访问量超过了设定的阈值,服务请求不会被立即失败,而是被放入一个队列内等待处理,如果服务请求在预设的超时时间内仍然未被处理,那么就会被移出队列。
限流是挡在降级熔断之前的一道关卡,它是投入产出比最高的防护措施,为什么这样说呢?我们将限流和降级做一个比对:在熔断阶段,用户流量已经打到了服务器,尽管我们对下游服务的调用不会真实发起,但上游服务的计算资源实打实的被占用了;而限流则不同,如果用户流量在入口处就被限制,那么它并不会占用服务器的资源来处理这个请求。
Sentinel 工作原理
sentinel在内部创建了一个责任链,责任链是由一系列ProcessorSlot对象组成的,每个ProcessorSlot对象负责不同的功能,外部请求是否允许访问资源,需要通过责任链的校验,只有校验通过的,才可以访问资源,如果被校验失败,会抛出BlockException异常。
sentinel提供了8个ProcessorSlot的实现类,下面实现类功能介绍:
- DegradeSlot:用于服务降级,如果发现服务超时次数或者报错次数超过限制,DegradeSlot将禁止再次访问服务,等待一段时间后,DegradeSlot试探性的放过一个请求,然后根据该请求的处理情况,决定是否再次降级。
- AuthoritySlot:黑白名单校验,按照字符串匹配,如果在黑名单,则禁止访问。
- ClusterBuilderSlot:构建ClusterNode对象,该对象用于统计访问资源的QPS、线程数、异常、响应时间等,每个资源对应一个ClusterNode对象。
- SystemSlot:校验QPS、并发线程数、系统负载、CPU使用率、平均响应时间是否超过限制,使用滑动窗口算法统计上述这些数据。
- StatisticSlot:用于从多个维度(入口流量、调用者、当前被访问资源)统计响应时间、并发线程数、处理失败个数、处理成功个数等。
- FlowSlot:用于流控,可以根据QPS或者每秒并发线程数控制,当QPS或者并发线程数超过设定值,便会抛出FlowException异常。FlowSlot依赖于StatisticSlot的统计数据。
- NodeSelectorSlot:负责收集资源路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级、数据统计。
- LogSlot:打印日志。