一、什么是Spring Cloud Alibaba Sentinel?
-
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护、 热点防护等多个维度保护服务的稳定性。
-
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案,Sentinel 作为其核心组件之一,具有熔断与限流等一系列服务保护功能。
-
Spring Cloud Sentinel 里所有的资源都对应一个资源名称
resourceName
,每次资源调用都会创建一个Entry
对象。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 SphU API 显式创建。Entry 创建的时候,同时也会创建一系列功能插槽 slot chain,这些插槽有不同的职责:-
NodeSelectorSlot
负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级
-
ClusterBuilderSlot
用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据
-
StatisticSlot
用于记录、统计不同纬度的 runtime 指标监控信息
-
FlowSlot
用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制
-
AuthoritySlot
根据配置的黑白名单和调用来源信息,来做黑白名单控制
-
DegradeSlot
通过统计信息以及预设的规则,来做熔断降级
-
SystemSlot
通过系统的状态,例如 load1 等,来控制总的入口流量
-
二、两大组成部分
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard):基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
三、它有哪些特性?
-
丰富的应用场景
Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
-
完备的实时监控
Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
-
广泛的开源生态
Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
-
完善的 SPI 扩展点
Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
四、下载和使用
-
下载
下载地址
:https://github.com/alibaba/Sentinel/releasesSentinel控制台是一个轻量级的控制台应用,它可用于实时查看单机资源监控及集群资源汇总,并提供了一系列的规则管理功能,如流控规则、降级规则、热点规则等。
-
启动
java -jar sentinel-dashboard-1.6.3.jar
-
测试
访问地址
:http://localhost:8080,用户名密码默认是sentinel监控数据
五、基础使用
-
添加依赖
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!--搭配feign使用时需要--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
添加配置
server: port: 8081 spring: application: name: nacos-service cloud: nacos: discovery: server-addr: localhost:8848 service: ${spring.application.name} sentinel: # 是否开启服务的sentinel监控 enabled: true # 是否立即加载服务的sentinel监控 eager: true # 监听地址 transport: dashboard: localhost:8080 port: 8719 # 开启feign对sentinel的支持 feign: sentinel: enabled: true management: endpoints: web: exposure: include: '*'
-
编写控制器类
@RestController public class UserController { @GetMapping("/get/{id}") public Map<String, Object> getUser(@PathVariable Long id) { Map<String, Object> result = new HashMap<>(); result.put("type", "ok"); result.put("msg", "查询成功"); return result; } }
-
测试
多次访问地址
:http://localhost:8081/get/1,查看Sentinel监控
六、核心注解说明
@SentinelResource注解
属性
-
value
资源名称
-
entryType
entry类型
-
blockHandler
指定BlockException异常处理器方法
@GetMapping("/show/test1") @SentinelResource(value = "test1", blockHandler = "myBlockHandler") public String test1(Long id) { return "test1调用成功"; } public String myBlockHandler(Long id, BlockException blockException){ if(blockException instanceof FlowException){ return "test1不可达,出现限流!"; } if(blockException instanceof DegradeException){ return "test1不可达,出现熔断!"; } return "test1不可达,出现其它异常!"; }
-
blockHandlerClass
指定BlockException异常处理器类
@GetMapping("/show/test1") @SentinelResource( value = "test1", blockHandler = "myBlockHandler", blockHandlerClass = SentinelBlockHandler.class ) public String test1(Long id) { System.out.println(1/0); return "test1调用成功"; }
public class SentinelBlockHandler { public String myBlockHandler(Long id, BlockException blockException){ if(blockException instanceof FlowException){ return "test1不可达,出现限流!"; } if(blockException instanceof DegradeException){ return "test1不可达,出现熔断!"; } return "test1不可达,出现其它异常!"; } }
-
fallback
指定内部异常处理器方法
@GetMapping("/show/test1") @SentinelResource(value = "test1", fallback = "myFallback") public String test1(Long id) { System.out.println(1/0); return "test1调用成功"; } public String myFallback(Long id){ return "test1不可达,出现内部调用异常!"; }
-
fallbackClass
指定内部异常处理器类
@GetMapping("/show/test1") @SentinelResource( value = "test1", fallback = "myFallback", fallbackClass = SentinelFallbackHandler.class ) public String test1(Long id) { System.out.println(1/0); return "test1调用成功"; }
public class SentinelFallbackHandler { public String myFallback(Long id){ return "test1不可达,出现内部调用异常!"; } }
-
defaultFallback
指定内部异常默认处理器
-
exceptionsToIgnore
忽略指定的内部异常出现,不做fallback处理
@GetMapping("/show/test1") @SentinelResource( value = "test1", fallback = "myFallback", exceptionsToIgnore = {NullPointerException.class} ) public String test1(Long id) { if (id == 1) { throw new IndexOutOfBoundsException(); } else if (id == 2) { throw new NullPointerException(); } return "test1调用成功"; } public String myFallback(Long id){ return "test1不可达,出现内部调用异常!"; }
@SentinelRestTemplate注解
@Configuration
public class RibbonConfig {
@Bean
@SentinelRestTemplate
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
七、熔断降级功能
Sentinel 支持对服务间调用进行保护,对故障应用进行熔断操作
-
配置示例
-
属性说明
Field 说明 默认值 resource 资源名,即规则的作用对象 grade 熔断策略,支持慢调用比例/异常比例/异常数策略 count 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 timeWindow 熔断时长,单位为 s minRequestAmount 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) 5 statIntervalMs 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) 1000 ms slowRatioThreshold 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) -
降级策略
-
平均响应时间
当指定资源的每秒请求量 >= N(默认为5,可以在启动sentinel时通过 -Dcsp.sentinel.statistic.max.rt=xxx 来设置)个时,如果这N个请求的平均响应时间如果超过指定的RT时间,就会触发熔断降级;如果此时在指定的时间窗口时间内没有任何请求,则熔断关闭,服务恢复正常。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M3oUyo2X-1616478058783)(Spring Cloud Alibaba.assets/20210321223354.png)]
-
异常比例
当指定资源的每秒请求量 >= N个时,如果在这N个请求处理中有超过指定比例的异常数出现,则就会触发熔断降级;如果此时在指定的时间窗口时间内没有任何请求,则熔断关闭,服务恢复正常。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iXvB0c4O-1616478058784)(Spring Cloud Alibaba.assets/20210321223926.png)]
-
异常数
在一分钟之内,如果指定资源的请求处理中有超过指定的异常数出现,则就会触发熔断降级;如果此时在指定的时间窗口时间内没有任何请求,则熔断关闭,服务恢复正常。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TFF31pHt-1616478058785)(Spring Cloud Alibaba.assets/20210321225020.png)]
-
八、流量控制功能
-
限流原理
- 原理就是监控应用流量的QPS或并发线程数等指标,当达到指定的阈值时对请求进行控制,以避免瞬间的流量高峰,从而保证了系统的高可用。
- 同一个资源可以创建多条限流规则,FlowSlot会对该资源的所有限流规则进行遍历,直到有规则触发限流或者所有规则遍历完毕。
-
配置示例
-
属性说明
资源名(resource)
:限流规则的作用对象针对来源(limitApp)
:若为default,则不区分流量来源,即为所有服务阈值类型(grade)
:限流规则的种类- QPS:每秒执行的请求数
- 线程数:单位时间内并发执行的线程数
单机阈值(count)
:限流规则的数值是否集群(cluster)
:当前访问资源是否为集群流控模式(strategy)
:触发限流后的执行策略- 直接:指的是当流量控制规则达到指定阈值后,当前资源请求直接触发相应的处理方式
- 关联:指的是我们为当前资源指定一个关联的资源,当关联的资源请求流量达到阈值时,当前的资源请求才会触发相应的处理方式
- 链路:指的是如果当前的资源请求会被多个服务链路进行调用,而我们只想对某个链路进行流量控制,那么就可以指定入口资源,也就是指定请求该资源的上一级请求路径。
流控效果(controllerBehavior)
:触发限流后的执行方式- 快速失败:默认的执行方式,请求直接被拒绝并抛出异常
- WarmUp:预热/冷启动方式,当长时间没有对该资源进行请求而某一时刻突然请求时,不会立马对所有的请求进行处理,而是在设定的预热时间里缓慢的进行处理直到阈值,之后再稳定的处理所有的请求
- 排队等待:匀速的排队等待处理,也就是在设定的超时时间内,如果资源的流量请求达到了阈值则剩下的请求会被排队等待处理;而如果还有请求没有处理并超过了超时时间,则直接拒绝并抛出异常
-
编写控制器类
@RestController @RequestMapping("/rateLimit") public class RateLimitController { /** * 按资源名称限流,需要指定限流处理逻辑 */ @GetMapping("/byResource") @SentinelResource(value = "byResource") public CommonResult byResource() { return new CommonResult("按资源名称限流", 200); } /** * 按访问路径限流,有默认的限流处理逻辑 */ @GetMapping("/byUrl") @SentinelResource(value = "byUrl") public CommonResult byUrl() { return new CommonResult("按url限流", 200); } }
-
按资源名称限流
-
按访问路径限流
-
九、集群流控功能
-
简介
- 假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用,这就是最基础的集群流控的方式。
- 假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
-
两种身份
-
Token Client
集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
-
Token Server
集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
-
-
流控规则定义
-
客户端
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId, parser); FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
-
服务端
ClusterFlowRuleManager.setPropertySupplier(namespace -> { return new SomeDataSource(namespace).getProperty(); });
-
-
启动方式
-
独立模式
作为独立的 token server 进程启动,独立部署,隔离性好,但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。
-
嵌入模式
作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署,灵活性比较好。但是隔离性不佳,需要限制 token server 的总 QPS,防止影响应用本身。嵌入模式适合某个应用集群内部的流控。
-
-
分配配置
Token Server
-
限流客户端
-
添加依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-cluster-client-default</artifactId> <version>1.8.0</version> </dependency>
-
-
限流服务端
-
添加依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-cluster-server-default</artifactId> <version>1.8.0</version> </dependency>
-
十、网关流控功能
-
简介
Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。
-
限流规则
GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
-
resource
资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
-
resourceMode
规则是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME),默认是 route。
-
grade
限流指标维度,同限流规则的 grade 字段。
-
count
限流阈值
-
intervalSec
统计时间窗口,单位是秒,默认是 1 秒。
-
controlBehavior
流量整形的控制效果,同限流规则的 controlBehavior 字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。
-
burst
应对突发请求时额外允许的请求数目。
-
maxQueueingTimeoutMs
匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
-
paramItem
参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。其中的字段:
- parseStrategy:从请求中提取参数的策略,目前支持提取来源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。
- fieldName:若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。
- pattern:参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。(1.6.2 版本开始支持)
- matchStrategy:参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2 版本开始支持)
-
-
实现原理
- 外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,其中会依次进行路由/API 分组匹配**、**请求属性解析和参数组装。
- Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序组装参数数组,最终传入
SphU.entry(res, args)
中。 - Sentinel API Gateway Adapter Common 模块向 Slot Chain 中添加了一个
GatewayFlowSlot
,专门用来做网关规则的检查。 GatewayFlowSlot
会从GatewayRuleManager
中提取生成的热点参数规则,根据传入的参数依次进行规则检查。若某条规则不针对请求属性,则会在参数最后一个位置置入预设的常量,达到普通流控的效果。
-
测试使用
在 API Gateway 端,用户只需要在原有启动参数的基础上添加如下启动参数即可标记应用为 API Gateway 类型:
# 注:通过 Spring Cloud Alibaba Sentinel 自动接入的 API Gateway 整合则无需此参数 -Dcsp.sentinel.app.type=1
-
Spring Cloud Gateway
-
添加依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId> <version>x.y.z</version> </dependency>
-
-
Spring Cloud Zuul
-
添加依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-zuul-adapter</artifactId> <version>x.y.z</version> </dependency>
-
十一、配置规则持久化存储
默认情况下在Sentinel控制台中配置规则时,控制台推送规则方式是通过API将规则推送至客户端并直接更新到内存中。一旦我们重启应用,规则将消失。我们可以将配置规则进行持久化,以存储到Nacos为例。
-
原理
-
使用
-
添加依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
-
添加配置
spring: cloud: sentinel: datasource: ds1: nacos: server-addr: localhost:8848 dataId: ${spring.application.name}-sentinel groupId: DEFAULT_GROUP data-type: json rule-type: flow
-
Nacos配置
添加如下内容
[ { // 资源名称 "resource": "/rateLimit/byUrl", // 来源应用 "limitApp": "default", // 阈值类型,0表示线程数,1表示QPS "grade": 1, // 单机阈值 "count": 1, // 流控模式,0表示直接,1表示关联,2表示链路 "strategy": 0, // 流控效果,0表示快速失败,1表示Warm Up,2表示排队等待 "controlBehavior": 0, // 是否集群 "clusterMode": false } ]
-
测试
启动服务以及Sentinel面板
快速访问测试接口
-
【源码地址】:GitHub