1 什么是Sentinel
Sentinel,中文翻译为哨兵,是为微服务提供流量控制、熔断降级的功能,它和Hystrix提供的功能一样,可以有效的解决微服务调用产生的“雪崩”效应,为微服务系统提供了稳定性的解决方案。随着Hytrxi进入了维护期,不再提供新功能,Sentinel是一个不错的替代方案。通常情况,Hystrix采用线程池对服务的调用进行隔离,Sentinel才用了用户线程对接口进行隔离,二者相比,Hystrxi是服务级别的隔离,Sentinel提供了接口级别的隔离,Sentinel隔离级别更加精细,另外Sentinel直接使用用户线程进行限制,相比Hystrix的线程池隔离,减少了线程切换的开销。另外Sentinel的DashBoard提供了在线更改限流规则的配置,也更加的优化。
从官方文档的介绍,Sentinel 具有以下特征:
丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring
Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。
2 Sentinel控制台
Sentinel控制台提供一个轻量级的控制台,它提供机器发现、单机资源实时监控、集群资源汇总,以及规则管理的功能. Sentinel DashBoard下载地址:https://github.com/alibaba/Sentinel/releases
下载完成后,以以下的命令启动
# 此处修改默认端口为18080
java -Dserver.port=18080 -Dcsp.sentinel.dashboard.server=localhost:18080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
启动成功后,在浏览器上访问localhost:18080,就可以显示Sentinel的登陆界面,登陆名为sentinel,密码为sentinel。
需要访问一次注册的服务才能显示出来,因为Sentinel是懒加载的;
2 如何在Spring Cloud中使用Sentinel
Sentinel作为Spring Cloud Alibaba的组件之一,在Spring Cloud项目中使用它非常的简单。现在以案例的形式来讲解如何在Spring
Cloud项目中使用Sentinel。本项目是在之前教程的案例基础上进行改造。在工程的pom文件加上sentinel的Spring
Cloud起步依赖,代码如下:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
在工程的配置文件application.yml文件中配置,需要新增2个配置:
spring.cloud.sentinel.transport.port: 8719 ,这个端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了1个限流规则,会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中。
spring.cloud.sentinel.transport.dashboard: 8080,这个是SentinelDashBoard的地址。
spring
sentinel:
transport:
port: 18720 # 交互端口,默认8719 占用可往上+1,直到可用
dashboard: localhost:18080 #控制台实际启动的地址
此时调用接口就可用在Sentinel控制台看到
需要注意的是,被限流的时候FeignClient并不会调用provider的接口,而是在consumer工程里直接报错。
3 配置限流
代码中配置限流
没有控制台上面配置的灵活,此处只了解一下用法,不去深究
public static void main(String[] args) {
// 配置规则.
initFlowRules();
while (true) {
// 1.5.0 版本开始可以直接利用 try-with-resources 特性,自动 exit entry
try (Entry entry = SphU.entry("HelloWorld")) {
// 被保护的逻辑
System.out.println("hello world");
} catch (BlockException ex) {
// 处理被流控的逻辑
System.out.println("blocked!");
}
}
}
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
控制台配置限流
新增流控规则
- 资源名:唯一名称。默认为请求路径 @SentinelResource注解修饰时,取里面的value值
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名。默认default
- QPS:每秒钟的请求数量,达到阈值进行限流
- 线程数:请求线程数,达到阈值时限流
- 直连:达到限流值直接限流
- 关联:当关联资源达到阈值时限流自己
- 链路:当某个接口过来的资源达到限流条件,触发限流
- 快速失败:直接失败,抛出异常
- Warm Up:根据codeFactor(冷加载因子,默认为3)的值,从阈值/codeFactor,经过预热时常,才达到设置的QPS阈值,避免突然大流量造成服务器的宕机!
- 排队等待:匀速排队,让请求匀速通过,阈值为QPS才生效,其次还可以设置一个超时等待时间,超时将被抛弃
新增降级规则
- 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断;
- 比例阈值:慢调用统计数对于最小请求数的占有比例;
注:比例阈值是Sentinel1.8.x版本的,如果比例阈值修改不生效/降级失败,需要将要将应用中的Sentinel的依赖改到相应的版本;
关于Spring Cloud Alibaba 各组件的适配版本,参考:[https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E]
比例阈值修改无效,参考:[https://github.com/alibaba/Sentinel/issues/1777] - 熔断时长:超过时间后会尝试恢复;
- 最小请求数:触发熔断的最小请求数目,若当前统计窗口内的请求数小于此值,即使达到了熔断条件也不会触发;
- 比例阈值:慢调用统计数对于最小请求数的占有比例;
- 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%;
- 比例阈值:异常数对于最小请求数的占有比例;
- 熔断时长:超过时间后会尝试恢复;
- 最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断;
- 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断;
- 异常数:统计的异常数;由于异常数是按照分钟统计的个数,时间窗口中的设置也必须要大于60s。不然会出现:结束熔断保护后仍可能继续熔断保护(不会释放!)
- 熔断时长:超过时间后会尝试恢复;
- 最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断;
新增热点规则
-
参数索引:对应controller中方法的下标,携带此参数的请求会被限流
-
参数类型:方法中的参数类型
-
参数值:上面的参数等于这个参数值时放宽规则
-
限流阈值:参数等于参数值里面配置的值时的限流阈值
注意配置完后点击添加
新增系统保护规则
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
- CPU usage(1.5.0 版本) 当系统 CPU 使用率超过阈值即触发系统保护(取值范围%200.0-1.0),比较灵敏。
新增授权规则
- 流控应用:微服务名称
- 授权类型:白名单指只有上面配置的应用访问,黑名单只上面配置的应用不能访问
4 @SentinelResource
注意:注解方式不支持 private 方法。
@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() + "\t 服务不可用");
}
//无兜底
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl()
{
return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
}
}
有兜底的,按资源名配置:兜底方法优先
无兜底的通过访问的URL来限流,会返回Sentinel自带默认的限流处理信息(直接抛出异常)
规律:
有兜底方法的按照资源名来,没有兜底方法的按照uri来
使用:只要使用了@SentinalResource 就配置一个兜底方法“BlockHandler”
触发异常产生的报错类型
Sentinel 限流降级本身的异常
BlockException
限流异常
FlowException
降级异常
DegradeException
参数限流异常
ParamFlowException
系统负载异常
SystemBlockException
授权异常
AuthorityException
关于@SentinelResource 注解,有以下的属性:
- value:资源名称,必需项(不能为空)
- entryType:entry类型,可选项(默认为 EntryType.OUT)
- blockHandler 对应处理 BlockException 的函数名称,可选项。若同时配置了 fallback和 blockHandler ,则只有 blockHandler会生效。
要求:- blockHandler 函数访问范围需要是 public,
- 返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。
- blockHandler 函数默认需要和原方法在同一个类中,如果希望使用其他类的函数,则需要指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
- fallback:fallback函数名称,可选项,用于在抛出异常的时候提供 fallback处理逻辑。fallback函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback函数签名和位置要求:
- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable类型的参数用于接收对应的异常。
- fallback函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass为对应的类的 Class 对象,注意对应的函数必需为 static函数,否则无法解析。
- defaultFallback(since 1.6.0):默认的 fallback函数名称,可选项,通常用于通用的 fallback逻辑(即可以用于很多服务或方法)。默认 fallback函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback和 defaultFallback,则只有 fallback会生效。defaultFallback函数签名要求:
- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
- defaultFallback函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 defaultFallbackClass为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
- exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
5 Sentinel持久化
配置进nacos
pom
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
yml
spring
sentinel:
transport:
port: 18720 # 交互端口,默认8719 占用可往上+1,直到可用
dashboard: localhost:18080 #控制台实际启动的地址
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service # 配置文件信息
groupId: DEFAULT_GROUP # 配置文件信息
data-type: json
rule-type: flow
nacos添加配置
[
{
"resource": "/testA",
"limitApp": "default",
"grade": 1,
"count": 5,
"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可以研究参考这篇文章
Sentinel1.8.0配置持久化到Nacos(基于push模式)
Q.E.D.