Sentinel: 分布式系统的流量防卫兵
Sentinel 是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
sentinel 中的熔断降级分为两种
使用sentinel主要还是重点关注其流控熔断,对于java运行时的fallback可以直接使用统一异常处理来解决。让专业的sentinel来做专业的流控。那些运行时的事情就不要他操心了。
另外值得注意的是。作为熔断机制 sentinel并没有半开状态。
github下载地址:https://github.com/alibaba/Sentinel/tags
本次以1.7.1为基础作为实验
下载:sentinel-dashboard-1.7.1.jar
对应的springcloud springboot 以及 springcloud-alibaba 版本为
org.springframework.boot
spring-boot-dependencies
2.2.5.RELEASE
pom
import
org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR1
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.2.0.RELEASE
pom
import
下载完成后 启动本地nacos
并使用java -jar 命令来启动sentinel (它使用的默认端口为8080)
启动完成后 访问 http://localhost:8080 用户名密码默认都是sentinel
POM
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
yml
server:
port: 8001
spring:
application:
name: boot-server
cloud:
nacos: # 配置nacos
discovery: # 服务发现地址
server-addr: 127.0.0.1:8848 # nacos 启动地址
sentinel: # 配置sentinel
transport:
dashboard: 127.0.0.1:8080 # 配置sentinel dashboard 启动地址
port: 8719 # 直接写8719 端口号默认是8719 如果被占用就是+1 直到找到未被占用端口
filter:
url-patterns: /** # 默认是/* 这里改成/** 则会监控所有的请求
这本地的boot-server微服务就被sentinel监控了
编写一个controller
@Slf4j
public class FallbackController {
@GetMapping("/hot_key/{id}")
@SentinelResource(value = "fallback|hot_key",blockHandler = "testHotKeyFallback")
@ApiOperation("热点限流")
public ResponseEntity testHotKey(@PathVariable("id") String id) throws Exception {
log.info("进入了 热点限流 测试方法");
return ResponseEntity.ok(ResponseEntityBody.create("热点限流方法:" + id));
}
public ResponseEntity testHotKeyFallback(String id, BlockException exception) throws Exception {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(ResponseEntityBody.create(id + "热点限流了!!"));
}
@GetMapping("/rt1")
@ApiOperation("RT,这里要在sentinel中设置 RT小于300 否则不能触发熔断,指定自定义限流方法1")
// 指定全局限流降级方法
@SentinelResource(value = "resource_rt1", blockHandlerClass = MyCustomLimiterHandler.class, blockHandler = "globalFallback1")
public ResponseEntity testRT() {
log.info("进入了RT测试方法");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return ResponseEntity.ok(ResponseEntityBody.create("A"));
}
@GetMapping("/rt2")
@ApiOperation("RT,这里要在sentinel中设置 RT小于300 否则不能触发熔断,指定自定义限流方法2")
// 指定全局限流降级方法
@SentinelResource(value = "resource_rt2", blockHandlerClass = MyCustomLimiterHandler.class, blockHandler = "globalFallback2")
public ResponseEntity testRT2() {
log.info("进入了RT测试方法");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return ResponseEntity.ok(ResponseEntityBody.create("A"));
}
@GetMapping("/rate")
@ApiOperation("异常比例: 这里是 50%的出错概率 在sentinel中 设置低于50%的错误率就可以触发熔断降级")
public ResponseEntity testRate() throws Exception {
log.info("进入了 异常比例 测试方法");
throw CommonException.create(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ResponseEntityBody.create("出错了!")));
}
@GetMapping("/err_num")
@ApiOperation("异常数: 这里一定会报错 所以 触发熔断后 不会再进入这个方法内 直到下一个窗口期")
public ResponseEntity testErrNum() throws Exception {
log.info("进入了 异常数 测试方法");
throw CommonException.create(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ResponseEntityBody.create("出错了!")));
// return ServerResponse.createBySuccessMessage("B");
}
}
统一的降级方法
public class MyCustomLimiterHandler {
public static ResponseEntity globalFallback1(BlockException exception) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(ResponseEntityBody.create("(Global)服务器太忙 限流 返回限流方法1 "));
}
public static ResponseEntity globalFallback2(BlockException exception) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(ResponseEntityBody.create("(Global)服务器太忙 限流 返回限流方法2 "));
}
}
因为sentinel是懒加载机制,所以需要访问一次访问才能看到簇点列表。
可以在簇点列表中对服务设置限流策略。
添加@SentinelResource注解
注意 使用SentinelResource注解 一定要加资源名称value字段 否则不起作用 ,
因为默认在sentinel控制台是将限流规则配置在url上的,而url配置后 是默认的降级返回。
所以不会找到注解的资源名对应的降级方法
最后会以默认的方法返回降级
平均响应时间超过设定的阈值 并且在时间窗口内通过的请求大于5 则触发熔断降级
窗口期过后 关闭断路器 回复服务
RT的最大值为 4900s 可用通过 -Dcsp.sentinel.statistic.max.rt=xxx 设置最大数
QPS>=5 并且 异常比例(每秒统计) 超过阈值 则触发熔断降级
窗口期过后 关闭断路器 回复服务
按分钟来统计服务异常数,如果超过阈值则触发熔断降级,
窗口期过后 关闭断路器 回复服务
可以设置某个资源的请求qps限流
比如:product/query/{id}
注解中的value就是资源名称
方法中的参数 从0开始 可以指定是否传递某个参数 限流
也可以指定某个参数 的值为多少 限流
比如 可以指定 id = 1 qps 不能超过 10 则每秒只能有10个请求 访问该资源。
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,
结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,
通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
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 达到阈值即触发系统保护。
系统级限流规则应该视情况添加。
fallback java运行时异常降级 这个配置与blockHandler相似,也可以使用全局异常处理代替
防止每次启动微服务都会丢失之前的限流配置
pom 增加依赖
com.alibaba.csp
sentinel-datasource-nacos
yml增加配置
spring:
cloud:
datasource: # 配置sentinel 流控规则持久化
ds1: # 数据库1
nacos: # 持久化到 nacos
server-addr: 127.0.0.1:8848 # nacos 地址
dataId: ${spring.application.name} # 数据id
data-type: json # 格式json
rule-type: flow # 流控规则
# namespace: public # 使用默认的 命名空间
# groupId: DEFAULT_GROUP # 使用默认分组
然后在nacos中添加配置文件json
[
{
"resource": "resource_rt1",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource:资源名
limitApp:流控针对的调用来源,default不区分来源
grade:限流阈值类型(0-根据并发数量来限流 1-根据QPS来进行流量控制)
count:限流阈值
strategy:调用关系限流策略
controlBehavior:流量控制效果(直接拒绝、WarmUP、匀速排队)
clusterMode:是否集群模式