-
Sentinel的作用
用于流量控制、熔断降级等
-
安装与简单使用
-
git下载Sentinel控制台https://github.com/alibaba/Sentinel/releases/tag/1.8.3,下载后直接使用java -jar 命令启动
-
项目引入sentinel依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>${spring-cloud-alibaba.version}</version> </dependency>
-
配置文件配置sentinel控制台地址
spring: cloud: sentinel: transport: dashboard: localhost:8080 #配置Sentinel dashboard地址
-
启动服务,然后查看Sentinel控制台,发现是什么都没有
访问接口后出现服务,因为它是懒加载的
-
-
Sentinel的控制规则
-
流控规则
-
流控模式:
直接: api达到限流条件时,直接限流
关联: 当关联的资源达到阈值时,就限流自己
链路: 只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源 -
流控效果:
快速失败: 直接失败,抛异常
Warm Up: 根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFctor,经过预热时长,才达到设置的QPS阈值
排队等待: 匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
新增如下规则
每秒点击一次接口:
一秒点击多次
接口被sentinel拦截并返回这个默认值
-
-
使用@SentinelResource注解修改Sentinel流控默认返回值
在资源方法上标注@SentinelResource注解配置流控规则可以让资源限流后进入指定的限流方法以覆盖Sentinel的默认处理
/** * @SentinelResource注解说明 * value:资源名,在sentinel控制台新增流控规则时填写的资源名 * blockHandler:被限流后要执行的方法,也称为兜底方法 * blockHandlerClass:兜底方法所在类,如果与资源方法在同一个类中可以省略不写 */ @GetMapping("/getOrderNo") @SentinelResource(value = "getOrderNoResource", blockHandler = "getOrderNoFailed", blockHandlerClass = UserController.class) public AjaxResult getOrderNo(String userId){ return AjaxResult.success(); } /** * 限流后执行的方法,需要和接口方法具有相同返回值和参数列表,并在参数列表最后添加一个BlockException类型参数。并且需要为static方法 */ public static AjaxResult getOrderNoFailed(String userId, BlockException exception){ return AjaxResult.error("当前访问人数过多,请稍候再试"); }
-
服务重启后发现在Sentinel控制台配置的流控规则也没有了,还需要重新配置,不是持久化的
配置规则持久化
-
引入sentinel-datasource-nacos依赖
<!--sentinel-datasource-nacos 用于sentinel流控规则持久化--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <version>1.6.3</version> </dependency>
-
在配置文件中配置数据源
spring: profiles: active: dev cloud: sentinel: transport: dashboard: localhost:8080 #配置Sentinel dashboard地址 datasource: # 为sentinel添加nacos数据源配置 ds1: nacos: server-addr: localhost:8848 data-id: ${spring.application.name} data-type: json rule-type: flow
-
在nacos中新增配置
-
这样sentinel就能自动去nacos中获取流控规则配置了,服务重启后规则仍然存在
-
-
熔断规则
-
在代码中使用注解资源id的方式先定义一个规则名称
@GetMapping("/sentinelA") @SentinelResource(value = "sentinelAResource", blockHandlerClass = UserControllerHandler.class, blockHandler = "sentinelA") public AjaxResult sentinelA(){ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } return AjaxResult.success(); }
-
兜底方法
public static AjaxResult sentinelA(BlockException exception) { return AjaxResult.error("限流 " + exception.getRule()); }
-
在sentinel控制台新建熔断规则(使用慢调用比例的策略)
- 最大RT:即请求响应时间
- 比例阈值:设置在指定请求次数(最小请求数)中响应时间超过最大RT的请求比例。值为0.1-1.0,对应1%-100%。就是说在最小请求数次请求中有百分之多少的请求响应时间超过最大RT
- 熔断时长:当sentinel开启熔断后持续多长时间
- 最小请求数:在一个时间窗口中最小的请求数
- 统计时长:指定统计时间窗口大小
-
当比例阈值设置为0.5,其他参数如上图设置,即说明在5s内最少有2个请求经过,如果其中的50%个请求响应时间超过200ms则启动熔断机制,也就是说2个请求中有一个响应时间超过200ms那么下一次请求就会被熔断,持续时间为4s
-
示例
第一次:
第二次:
-
-
热点规则
-
在代码中使用注解资源id的方式先定义一个规则名称
/** * 测试sentinel热点规则 */ @GetMapping("/sentinelHotspot") @SentinelResource(value = "sentinelHotspotResource", blockHandlerClass = UserControllerSentinelHandler.class, blockHandler = "sentinelHotspot") public AjaxResult sentinelHotspot(@RequestParam(name = "userId", required = false) String userId ,@RequestParam(name = "orderId", required = false) long orderId){ return AjaxResult.success("sentinelHostspot获取成功"); }
-
兜底方法
public static AjaxResult sentinelHotspot(@RequestParam(name = "userId", required = false) String userId ,@RequestParam(name = "orderId", required = false) long orderId, BlockException exception){ return AjaxResult.error("可能为恶意访问,热点限流:" + exception.getRule()); }
-
在sentinel控制台新建热点规则
- 参数索引:设置方法中指定参数,对应上边的方法即userId这个参数
- 单机阈值:在指定统计窗口时长访问带指定参数的方法的次数
- 对于上边代码这个设置即为:在3s内访问sentinelHotspot()接口并且带有userId参数的次数如果达到2后边的方法就限流
-
示例
第一次和第二次都可以正常返回:
第三次:
{"msg":"可能为恶意访问,热点限流:ParamFlowRule{grade=1, paramIdx=0, count=2.0, controlBehavior=0, maxQueueingTimeMs=0, burstCount=0, durationInSec=3, paramFlowItemList=[], clusterMode=false, clusterConfig=ParamFlowClusterConfig{flowId=null, thresholdType=0, fallbackToLocalWhenFail=true, sampleCount=10, windowIntervalMs=1000}}","code":400}
-
-