官方文档
sentinel下载
Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
sentinel分为两个部分:
客户端:不依赖任何框架,能够运行于所有java运行时环境
控制台:基于spring boot开发,打包后直接运行,不需要额外的tomcat等应用
我们直接启动jar包就可以了,默认的访问端口是8080 账号密码:sentinel
父工程
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
.........
</dependencies>
</dependencyManagement>
//这三个依赖是必备的
创建子服务:
<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>
yml:
server:
port: 8003
spring:
application:
name: cloudsentinel-8003
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719
# clientIp: localhost:8003
#默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
management:
endpoints:
web:
exposure:
include: '*'
#我们上面搭配了nacos的注册中心,当然你不搭也可以,但是微服务是必须要有注册中心的,单纯的写个demo也可以不使用。
此时访问sentinel的管理后端是没有效果的,应为sentinel是懒加载
我们就写一个controller来访问一下
此时就会有数据了
资源名:唯一名称,默认请求路径
针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
阈值类型/单机阈值:
>直接:api达到限流条件时,直接限流(默认)
>关联:当关联的资源达到阈值时,就限流自己
>链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)[api级别的针对来源]
流控效果:
>快速失败:直接失败;抛异常
>Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值codeFactor,经过预热时长,才达到设置的QPS阈值
>排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置QPS,否则无效
测试:给我们的/test接口请求添加限流设置
使用QPS
此时我们疯狂请求这个接口:
则已经帮我们限流了,访问不到
上面的是使用的是直接QPS和直接拒绝,线程数则是并发数,一直同一个浏览器或同一个窗口发请求是没有问题的,多个浏览器或开其他端口访问就会有问题了,这里就不累赘了,都是一样的配置。
上面我们使用的是直接拒绝,到达了阈值就直接返回错误信息,
那这个关联呢?
说明: 当/testb请求的阈值达到了2,那么他会导致/testa不可用,返回错误信息,
这种情况适用于一些链路调用,请求数过大或并发量过大,直接快速失败关联的请求接口
有快速失败,warm up 排队等待。
上面的例子都是使用的快速失败。
快速失败在页面上直接返回错误信息
它的配置在源码的:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController 类上
公式:阈值除以coldFactor(冷因子默认值为3),经过预热时长后才会达到阈值
这个图表示,当流量过来时,会以总的单机阈值 10/3(冷因子)=3,3是单机阈值,通过时长5秒慢慢的达到10的阈值
这个WarmUp的源码在com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController,
可以自己去配置他的冷因子
说明:排队等待只可以是QPS,不支持线程数。表示这个/testb每秒可接收5个请求,剩下的进行等待,而超时时间是500毫秒。
匀速排队:会严格的控制请求通过的间隙时间,让请求均匀速度通过,对应的是漏桶算法。
排队等待的源码实现:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
注意:1.8版本的sentinel和1.8一下的熔断降级有明显的变化,我这里使用的是1.7的,具体参考官方文档
资源名不多说了就是请求路径
RT(平均响应时间,秒级):
平均响应时间:1s内持续进入5个请求,对应时刻的平均响应时间均超过阈值(这个阈值就是我们每个请求不能超过的值,否则满足熔断条件之一),那么在接下的时间窗口之内,对这个方法的调用都会自动熔断(满足1s内5个请求,并且平均响应时间大于阈值才熔断)
RT默认最大4900ms(更大的需要通过 -Dcsp.sentinel.statistic.max.rt=xxxx才能生效)
异常比例(秒级)
当资源的每秒请求量>=5,并且每秒异常总数占通过的比值超过阈值,资源进入降级状态,即在接下的时间窗口之内,
对这个方法的调用都会自动的返回,异常比例的阈值范围[0.0,1.0] 表示10%~100%
异常数(分钟级)
当资源近一分钟的异常数超过阈值后会进行熔断,注意由于统计时间窗口是分钟级别的,
若时间窗口是小于60s,则结束熔断状态后仍可能在进入熔断,所有要设置大于60
时间窗口,就是熔断的时间,到了时间,则会退出熔断,再根据策略等原来判断是否再次熔断。
-----------------------------------
Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,
让请求快速失败,避免影响到其他的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegtadeException)
具体的就不演示了,
但是要注意,在异常比例的情况下:如果请求达不到5或者5以上,系统会报错但不会被熔断的。
文档地址
说明:何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。
源码:com.alibaba.csp.sentinel.slots.block.BlockException
@GetMapping("/test")
@SentinelResource(value = "test",blockHandler = "block") //blockHandel指定熔断需要访问的方法 ,注意这个value,我们有用
public String test(@RequestParam(value = "id") String id){
return "正常";
}
//上面的blockHandler指定的方法
public String block(String id, BlockException block){
System.out.println(block);
return "熔断";
}
这里和之前的兜底方法很相识
注意:使用了@SentinelResource注解,写了blockHandler属性指定了兜底的方法,当违背配置规则后,就执行指定的兜底方法,
前提再sentinel配置的时候我们指定的资源名称是value的情况下
如果我们在sentinel后台配置的情况下指定的资源名是我们的请求的路径,那么就会有一些变化
使用了@SentinelResource注解,有blockHandler属性指定了兜底的方法,当违背配置规则后,就执行指定的兜底方法,
没有指定的兜底方法的话就是系统默认的兜底方法
上面两种的细微差别
而且该注解不支持private的权限修饰的接口方法
参数索引就是我们的参数id索引位置。资源名称不是请求名称,而是我们配置的value.
上面的代码我们有一个参数id,那么我们携带其他的参数是没有效果的,也就是不限流
比如
url?id=3 限流
url?id=4&idd=6 限流
url?no=8 不限流
并且要注意的是:
@SentinelResource(value = "test",blockHandler = "block")
blockHandler 指定的方法,当接口出现异常跟会不会执行这个方法是没有关系的
会执行这个方法是违背了sentinel里面的热点key的配置才会执行的,接口报错和它没有必然关系
但想要有友好的返回效果,我们可以使用上面说到的降级规则。也就是说这个注解管不了我们的异常。
只有违背了配置才会调用这个指定的方法。
那么还有一些情况,虽然我们设置了参数,只有携带了指定的参数时,才会有兜底的方法,
sentinel还有一些其他的设置,比如当这个参数等于某个值的时候可以再对其设置阈值
如下:
在高级选项中
当这个参数的值等于5时,我们给的阈值等于100,就是1s内可以有100个请求都不会走兜底的方法。
》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 达到阈值即触发系统保护。
兜底方法改进
//如果我们的兜底方法和业务接口方法写到一起,那么就会造成高耦合的情况,我们应该将他们分开来
//首先先看我们的接口,
@GetMapping("/test")
@SentinelResource(value = "test",blockHandlerClass=MyBlockHandel.class ,blockHandler = "block")
public String test(@RequestParam(value = "id") String id){
return "正常";
}
新建一个类,类名对应着blockHandlerClass的属性值,方法名对应着blockHandler 的属性值
public class MyBlockHandel {
public static String block(String id,BlockException exception){
return "兜底";
}
.....
//当然这里还可以有其他的兜底方法,注意:方法参数和返回值要一样
}
上面讲了,使用@SentinelResource注解的blockHandler只有在违背了配置情况下才会执行兜底的方法,
但是如果代码发生了异常是不会执行兜底的方法的。
这个时候这个注解就有一个fallback属性可以配置
@GetMapping("/test")
@SentinelResource(value = "test",fallback="xxxx")
public String test(@RequestParam(value = "id") String id){
int i=10/0;
return "正常";
}
//这个方法的名字就是上面的fallback的值,表示上面的方法报异常了,会执行下面的方法,注意,参数要一样,throwable可加可不加
public String xxxx(@RequestParam(value = "id") String id,Throwable throwable){
return "异常";
}
当然除了fallback属性外,还有fallbackClass指定一个类,和之前的blockHandlerClass属性差不多
问题来了
注意:
如果配置了blockHandler(前提是在控制台配置了规则)和配置了fallback,那么发生了异常和QPS超过了怕配置会执行哪个兜底方法呢?
首先如果是违背了配置的规则,肯定是会执行blockHandler指定的方法的。
那么发生了异常呢会执行fallback指定的方法的。
如果我们的配置规则是QPS为1的时候就降级,然后手动代码中抛出异常,也就是同时满足上面两种情况的话,
是会执行blockHandler指定的方法的,不会执行fallback指定的方法,注意这里
忽略异常:
@SentinelResource还有一个属性exceptionsToIgnore,
@SentinelResource(value="dsd",fallback="xxx",exceptionsToIgnore={yyyException.class})
这种情况,尽管我们指定了fallback属性,报异常了走xxx方法,但是我们这里的exceptionsToIgnore属性指定了忽略的异常,
也就是说当发生这个指定的异常时,不会执行fallback指定的xxx方法,也就是返回 error page。因为不做这个异常的处理。
其次,如果想让Sentinel支持feign
还需要再配置文件中加上一些配置
feign:
sentinel:
enabled: true
现在的我们的Sentinel是默认没有持久化的,只要Sentinel重启里面的配置就会情况,
或者说当有关联的服务的一些配置信息在Sentinel上,只要服务重新启动,更这个服务相关的配置也会清空,这是非常不友好的,
所以我们需要配置持久化
添加坐标
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
配置文件:
server:
port: 8003
spring:
application:
name: cloudsentinel-8003
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719
#默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudsentinel-8003
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
然后配置好后,我们打开nacos的配置列表新增配置
dataID就填写我们上面写的dataId,然后配置格式写json,
内容填写为:
[
{
"resource": "/test",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource:资源名称
limitApp:来源应用
grade:阈值类型,0表示线程数,1表示QPS
count:单机阈值
strategy:流控模式:0表示直接;1表示关联;2表示链路
controlBehavior:流控效果:0表示快速失败;1表示Warm Up;2表示排队等待
clusterMode:是否集群
现在就这些数据持久化到nacos了,要保证nacos也是持久化的,此时服务启动后,配置也不会丢失了。但是要先访问一次资源。