一、简介
二、下载安装
打开上面的下载地址,下载jar包,使用java -jar命令运行(当然本地得安装好java运行环境)
安装好后,使用如下地址:http://localhost:8080/#/dashboard。默认用户名密码都是sentinel
三、初始化演示工程
新建Module:cloudalibaba-sentinel-service8401
1、POM
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2020</artifactId> <groupId>com.atguigu.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloudalibaba-sentinel-service8401</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>4.6.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
2、YML
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: 192.168.216.132:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
management:
endpoints:
web:
exposure:
include: '*'
3、主启动
@EnableDiscoveryClient @SpringBootApplication public class MainApp8401 { public static void main(String[] args) { SpringApplication.run(MainApp8401.class, args); } }
4、业务类FlowLimitController
@RestController public class FlowLimitController { @GetMapping("/testA") public String testA() { return "------testA"; } @GetMapping("/testB") public String testB() { return "------testB"; } }
- 启动Sentinel8080和Nacos8848
- 启动微服务8401,查看sentienl控制台,sentienl采用懒加载,需要执行一次微服务的请求后才会显示
效果如下
四、流控规则
1、基本介绍
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单机阈值:
- QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
- 线程数:当调用该api的线程数达到阈值的时候,进行限流
- 是否集群:不需要集群
- 流控模式
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)api级别的针对来源
- 流控效果:
- 快速失败:直接失败,抛异常
- Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
2、直接(默认)
配置及说明
快速点击访问:http://localhost:8401/testA
结果:Blocked by Sentinel (flow limiting)
3、关联
当关联的资源达到阈值时,就限流自己(当与A关联的资源B达到阈值后,就限流自己)通俗的讲:B惹事,A挂了
设置效果:当关联资源/testB的qps阈值超过1时,就限流/testA的Rest访问地址,当关联资源到阈值后限制配置好的资源名
4、链路
链路相比于上面的关联,可以看到最明显的变化是“关联资源”变成了“入口资源”,意思很明显:当入口资源B的调用超过了阈值,且资源A在B调用的链路上,就限流自己。
五、流控效果
1、直接->快速失败(默认的流控处理)
- 直接失败,抛出异常:Blocked by Sentinel (flow limiting)
- 源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
2、预热
- 公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值
- com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
-
应用场景如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。
3、排队等待
- 匀速排队,阈值必须设置为QPS
- com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
六、降级规则
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
1、Sentinel 提供以下几种熔断策略
- 慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。 - 异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。 - 异常数 (
ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException
)不生效。
2、RT实战
代码
@GetMapping("/testD") public String testD() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.info("testD 测试RT"); return "------testD"; }
配置如下:
按照上述配置:一秒中打进来10个线程(大于5个了)调用/testD,我们希望200毫秒处理完本次任务,如果超过200毫秒还没有处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了,后续没有这么大的访问量了,断路器关闭(保险丝恢复)微服务恢复OK。
3、异常比例
代码
@GetMapping("/testD") public String testD() { log.info("testD 测试RT"); int age = 10 / 0; return "------testD"; }
配置
4、异常数
代码
@GetMapping("/testE") public String testE() { log.info("testE 测试异常数"); int age = 10 / 0; return "------testE 测试异常数"; }
配置
七、热点参数限流
源码位置:com.alibaba.csp.sentinel.slots.block.BlockException
1、根据参数限流
代码
@GetMapping("/testHotKey") @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey") public String testHotKey(@RequestParam(value = "p1", required = false) String p1, @RequestParam(value = "p2", required = false) String p2) { //int age = 10/0; return "------testHotKey"; } //兜底方法 public String deal_testHotKey(String p1, String p2, BlockException exception) { return "------deal_testHotKey,o(╥﹏╥)o"; }
- @SentinelResource(value = "testHotKey"),异常打到了前台用户界面看不到,不友好
- @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey"),方法testHostKey里面第一个参数只要QPS超过每秒1次,马上降级处理,用了我们自己定义的
测试
- 超过阈值限流:http://localhost:8401/testHotKey?p1=abc
- 超过阈值限流:http://localhost:8401/testHotKey?p1=abc&p2=33
- 不限流:http://localhost:8401/testHotKey?p2=abc
2、参数例外项:参数如果为某个值不限流
上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流。我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样,比如:假如当p1的值等于5时,它的阈值可以达到200
注意:热点参数的注意点,参数必须是基本类型或者String
测试
- 不限流:http://localhost:8401/testHotKey?p1=5,当p1等于5的时候,阈值变为200
- 限流:http://localhost:8401/testHotKey?p1=3,当p1不等于5的时候,阈值就是平常的1
八、系统自适应限流
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN
),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的
maxQps * minRt
估算得出。设定参考值一般是CPU cores * 2.5
。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
九、@SentinelResource
@SentinelResource可以处理自定义的限流返回和熔断返回
1、限流返回形式一
@GetMapping("/byResource") @SentinelResource(value = "byResource", blockHandler = "handleException") public CommonResult byResource() { return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001")); } public CommonResult handleException(BlockException exception) { return new CommonResult(444, exception.getClass().getCanonicalName() + " 服务不可用"); }
2、限流返回形式二
@GetMapping("/rateLimit/customerBlockHandler") @SentinelResource(value = "customerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException") public CommonResult customerBlockHandler() { return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003")); }
单独定义限流异常处理类
package com.atguigu.springcloud.alibaba.myhandle; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.atguigu.springcloud.entities.CommonResult; public class CustomerBlockHandler { public static CommonResult handleException(BlockException exception) { return new CommonResult(2020, "自定义限流处理信息....CustomerBlockHandler"); } }
3、熔断返回形式一
@GetMapping("/fallback") @SentinelResource(value = "fallback", fallback = "handlerFallback") public CommonResult fallback() { int num = 1 / 0; return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003")); } public CommonResult handlerFallback(Throwable exception) { return new CommonResult(555, exception.getClass().getCanonicalName() + " 服务不可用"); }
4、熔断返回形式二
@GetMapping("/fallback") @SentinelResource(value = "fallback",
fallbackClass = CustomerFallbackHandler.class,
fallback = "handlerFallback") public CommonResult fallback() { int num = 1 / 0; return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003")); }
单独定义熔断异常处理类
public class CustomerFallbackHandler { public static CommonResult handlerFallback(Throwable exception) { return new CommonResult(2020, "自定义熔断处理信息....CustomerFallbackHandler"); } }
十、规则持久化
一旦我们重启应用,Sentinel规则将消失,生产环境需要将配置规则进行持久化。
其中一个解决方法:将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上Sentinel上的流控规则持续有效。
修改cloudalibaba-sentinel-service8401,步骤:
1、POM,增加依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
2、YML,添加Nacos数据源配置
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: 192.168.216.132:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
datasource:
ds1:
nacos:
server-addr: 192.168.216.132:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
3、添加Nacos业务规则配置
内容解析
测试接口:
@RestController public class RateLimitController { @GetMapping("/byResource") @SentinelResource(value = "byResource", blockHandler = "handleException") public CommonResult byResource() { return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001")); } public CommonResult handleException(BlockException exception) { return new CommonResult(444, exception.getClass().getCanonicalName() + " 服务不可用"); } }
4、测试
停止8401,发现规则消失了,启动8401,发现还是没有,但是调用几次接口后,规则又出现了