前言:为什么需要流控降级
我们的生产环境经常会出现一些不稳定的情况,如:
- 大促时瞬间洪峰流量导致系统超出最大负载,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 的调用频率。
Sentinel 有着丰富的开源生态。Sentinel 开源不久就被纳入 CNCF Landscape 版图,并且也成为 Spring Cloud 官方推荐的流控降级组件之一。社区提供 Spring Cloud、Dubbo、gRPC、Quarkus 等常用微服务框架的适配,开箱即用;同时支持 Reactive 生态,支持 Reactor、Spring WebFlux 异步响应式架构。Sentinel 也在逐渐覆盖 API Gateway 和 Service Mesh 场景,在云原生架构中发挥更大的作用。
在原来的 Spring Cloud Netflix 系列中,有自带的熔断组件 Hystrix ,是 Netflix 公司提供的一个开源的组件,提供了熔断、隔离的这些特性,不过 Hystrix 在 2018 年 11 月份开始,就不再迭代开发,进入维护的模式。同年开源的 Spring Cloud Alibaba (SCA) 提供了一站式的解决方案,默认为 Sentinel 整合了 Spring Web、RestTemplate、FeignClient 和 Spring WebFlux。Sentinel 在 Spring Cloud 生态中,不仅补全了 Hystrix 在 Servlet、RestTemplate 和 API Gateway 这一块的空白,而且还完全兼容了 Hystrix 在 FeignClient 中限流降级的用法,并且支持运行时灵活地配置和调整限流降级规则。同时 SCA 还集成了 Sentinel 提供的 API gateway 流控模块,可以无缝支持 Spring Cloud Gateway 和 Zuul 网关的流控降级。
Spring Cloud Alibaba Sentinel 服务限流/熔断实战
下面到了动手时间了!
我们已经将案例代码集成在沙箱中,通过点击链接直接访问。
我们结合一个实例来对 Spring Cloud 服务限流/熔断进行实战。我们的实例项目由四个模块构成:
- service-api: 服务接口定义,供 consumer/provider 引用
- dubbo-provider: Dubbo 服务端,对外提供一些服务
- web-api-demo: Spring Boot Web 应用,其中的一些 API 会作为 consumer 来调用 dubbo-provider 获取结果。里面一共定义了三个 API path:
/demo/hello
: 接受一个 name 参数,会 RPC 调用后端的FooService:sayHello(name)
方法。/demo/time
:调用后端的FooService:getCurrentTime
方法获取当前时间;里面可以通过 slow 请求参数模拟慢调用。/demo/bonjour/{name}
: 直接调用本地 DemoService 服务。- demo-gateway: Spring Cloud Gateway 网关,作为整个项目的访问入口,将流量转发至后端服务或第三方服务。我们的入口 URL 访问都会经过该 API gateway。demo-gateway 的路由配置如下:
spring: cloud: gateway: enabled: true discovery: locator: # route ID 转化小写 lower-case-service-id: true routes: - id: foo-service-route uri: http://localhost:9669/ predicates: - Path=/demo/** - id: httpbin-route uri: https://httpbin.org predicates: - Path=/httpbin/** filters: - RewritePath=/httpbin/(?<segment>.*), /$\{segment}
这个路由配置包含两个路由:
foo-service-route
: 会将/demo/
开头的访问路由到 localhost:9669 后端服务上面,即对应我们的 Web 服务。我们访问示例中的 API 都会经过这个路由,比如localhost:8090/demo/time
。httpbin-route
: 这是一个示例路由,它会将/httpbin/
开头的访问路由到 https://httpbin.org 这个示例网站上,比如localhost:8090/httpbin/json
实际会映射到https://httpbin.org/json
上面。
同时我们的环境也包含启动好的 Sentinel 控制台,可以直接访问并供各个服务接入。对应的地址:http://139.196.203.133:8080
下面我们来一步一步操作接入 SCA Sentinel 并通过控制台/Nacos 动态数据源配置流控降级规则来验证效果。
spring-cloud-alibaba-dependencies 配置
首先第一步我们在项目的父 pom 里面导入最新版本的 spring-cloud-alibaba-dependencies,这样我们在实际引入 SCA 相关依赖的时候就不需要指定版本号了:
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.2.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
服务接入 SCA Sentinel
首先我们分别为三个服务模块引入 Spring Cloud Alibaba Sentinel 依赖:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
starter 会自动对 Sentinel 的适配模块进行配置,只需要简单的配置即可快速接入 Sentinel 并连接到 Sentinel 控制台。
对于 Dubbo 服务,我们还需要额外引入 Dubbo 的适配模块。Sentinel 为 Apache Dubbo 提供开箱即用的整合模块,仅需引入 sentinel-apache-dubbo-adapter
依赖即可接入 Dubbo 自动埋点统计(支持 provider 和 consumer):
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-apache-dubbo-adapter</artifactId> <version>1.8.0</version> </dependency>