服务熔断和限流
# 服务熔断和限流
官方文档:Sentinel · alibaba/spring-cloud-alibaba Wiki (github.com) (opens new window)
# 前言
为什么需要流控降级?
我们的生产环境经常会出现一些不稳定的情况,如:
- 大促时瞬间洪峰流量导致系统超出最大负载,load 飙高,系统崩溃导致用户无法下单
- “黑马”热点商品击穿缓存,DB 被打垮,挤占正常流量
- 调用端被不稳定服务拖垮,线程池被占满,导致整个调用链路卡死
这些不稳定的场景可能会导致严重后果。大家可能想问:
- 如何做到均匀平滑的用户访问?
- 如何预防流量过大或服务不稳定带来的影响?
这时候我们就要请出微服务稳定性的法宝 —— 高可用流量防护,其中重要的手段就是【流量控制】和【熔断降级】,它们是保障微服务稳 定性重要的一环。
为什么需要流量控制?
流量是非常随机性的、不可预测的。前一秒可能还风平浪静,后一秒可能就出现流量洪峰了(例如双十一零点的场景)。
然而我们系统的容量总是有限的,如果突然而来的流量超过了系统的承受能力,就可能会导致请求处理不过来,堆积的请求处理缓慢,CPU/Load 飙高,最后导致系统崩溃。
因此,我们需要针对这种突发的流量来进行限制,在尽可能处理请求的同时来保障服务不被打垮,这就是流量控制。
为什么需要熔断降级?
一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。
例如,
- 支付的时候,可能需要远程调用银联提供的 API;
- 查询某个商品的价格,可能需要进行数据库查询。
然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。
# Sentinel: 高可用护航的利器
Sentinel 是阿里巴巴开源的,面向分布式服务架构的高可用防护组件,主要以流量为切入点,从流量控制、流量整形、熔断降级、系统自适应保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀、冷启动、消息削峰填谷、自适应流量控制、实时熔断下游不可用服务等,是保障微服务高可用的利器,原生支持 Java/Go/C++ 等多种语言,并且提供 Istio/Envoy 全局流控支持来为 Service Mesh 提供高可用防护的能力。
Sentinel 的技术亮点:
- 高度可扩展能力:基础核心 + SPI 接口扩展能力,用户可以方便地扩展流控、通信、监控等功能。
- 多样化的流量控制策略(资源粒度、调用关系、流控指标、流控效果等多个维度),提供分布式集群流控的能力。
- 热点流量探测和防护。
- 对不稳定服务进行熔断降级和隔离。
- 全局维度的系统负载自适应保护,根据系统水位实时调节流量。
- 覆盖 API Gateway 场景,为 Spring Cloud Gateway、Zuul 提供网关流量控制的能力。
- 实时监控和规则动态配置管理能力。
# 使用场景
- 在服务提供方(Service Provider)的场景下,我们需要保护服务提供方自身不被流量洪峰打垮。这时候通常根据服务提供方的服务能力进行流量控制,或针对特定的服务调用方进行限制。我们可以结合前期压测评估核心接口的承受能力,配置 QPS 模式的限流,当每秒的请求量超过设定的阈值时,会自动拒绝多余的请求。
- 为了避免调用其他服务时被不稳定的服务拖垮自身,我们需要在服务调用端(Service Consumer)对不稳定服务依赖进行隔离和熔断。手段包括信号量隔离、异常比例降级、RT 降级等多种手段。
- 当系统长期处于低水位的情况下,流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。这时候我们可以借助 Sentinel 的 WarmUp 流控模式控制通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,而不是在一瞬间全部放行。这样可以给冷系统一个预热的时间,避免冷系统被压垮。
- 利用 Sentinel 的匀速排队模式进行“削峰填谷”,把请求突刺均摊到一段时间内,让系统负载保持在请求处理水位之内,同时尽可能地处理更多请求。
- 利用 Sentinel 的网关流控特性,在网关入口处进行流量防护,或限制 API 的调用频率。
# 服务限流/熔断实战
实例项目由四个模块构成:
- service-api: 服务接口定义,供 consumer/provider 引用。
- dubbo-provider: Dubbo 服务端,对外提供一些服务。
- web-api-demo: Spring Boot Web 应用,其中的一些 API 会作为 consumer 来调用 dubbo-provider 获取结果。
# 流控降级组件对比
功能/特性 | Sentinel | Hystrix | resilience4j |
---|---|---|---|
隔离策略 | 信号量隔离(并发控制) | 线程池隔离/信号量 | 信号量隔离 |
熔断降级策略 | 基于慢调用比例、异常比例、异常数 | 基于异常比例 | 基于异常比例、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于 Rx Java) | Ring Bit Buffer |
动态规则配置 | 支持多种数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 | Rate Limiter |
流量整形 | 支持预热模式与匀速排队控制效果 | 不支持 | 简单的 Rate Limiter 模式 |
系统自适应保护 | 支持 | 不支持 | 不支持 |
多语言支持 | Java/Go/C++ | Java | Java |
Service Mesh 支持 | 支持 Envoy/Istio | 不支持 | 不支持 |
控制台 | 提供开箱即用的控制台,可配置规则、实时监控、机器发现等 | 简单的监控查看 | 不提供控制台,可对接 其它监控系统 |
# 最后
是不是服务的量级很小就不用进行限流防护了呢?
是不是微服务的架构比较简单就不用引入熔断保护机制了呢?
其实,这与请求的量级、架构的复杂程度无关。很多时候,可能正是一个非常边缘的服务出现故障而导致整体业务受影响,造成巨大损失。我们需要具 有面向失败设计的意识,在平时就做好容量规划和强弱依赖的梳理,合理地配置流控降级规 则,做好事前防护,而不是在线上出现问题以后再进行补救。
# 参考书籍
重磅下载 | Java 开发者必备手册《Spring Cloud Alibaba 从入门到实战》,阿里双11同款!-阿里云开发者社区 (aliyun.com) (opens new window)