一、概述
1.1 简介
sentinel是Spring Cloud Alibaba的一个重要组件,类似于spring clound的hystrix,与hystrix-dashboard控制台一样,sentinel-dashboard控制台可以提供对流量的实时监控、在线维护流量规则、熔断规则,前提是微服务整合了sentinel。
Sentinel 具有以下特征:
-
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
-
完备的实时监控:Sentinel 同时提供实时的监控功能。可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
-
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
-
完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
1.2 基本概念
1.2.1 资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
1.2.2 规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
1.3 主要功能
1.3.1 流量控制
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
流量控制有以下几个角度:
-
资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
-
运行指标,例如 QPS、线程池、系统负载等;
-
控制的效果,例如直接限流、冷启动、排队等。
1.3.2 熔断降级
除了流量控制以外,降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。
Sentinel 和 Hystrix 的处理原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。
熔断降级设计理念
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过线程池的方式,来对依赖(Sentinel 概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本,还需要预先给各个资源做线程池大小的分配。
Sentinel 对这个问题采取了两种手段:
-
通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
-
通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
1.3.3 系统负载保护
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
二、工作流程
总体的框架如下:
在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 API 显式创建;每一个 Entry 创建的时候,同时也会创建一系列功能插槽(slot chain)。这些插槽有不同的职责,例如:
-
NodeSelectorSlot
负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级; -
ClusterBuilderSlot
用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;-
可通过如下命令查看某个资源不同调用者的访问情况:
curl http://localhost:8719/origin?id=caller
:
-
-
StatisticSlot
是 Sentinel 的核心功能插槽之一,用于统计实时的调用数据;-
clusterNode
:资源唯一标识的 ClusterNode 的 runtime 统计 -
origin
:根据来自不同调用者的统计信息 -
defaultnode
: 根据上下文条目名称和资源 ID 的 runtime 统计 -
入口的统计
-
Sentinel 底层采用高性能的滑动窗口数据结构
LeapArray
来统计实时的秒级指标数据,可以很好地支撑写多于读的高并发场景。
-
-
FlowSlot
用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制。如果一个资源对应两条或者多条流控规则,则会根据如下次序依次检验,直到全部通过或者有一个规则生效为止:-
指定应用生效的规则,即针对调用方限流的;
-
调用方为 other 的规则;
-
调用方为 default 的规则。
-
-
AuthoritySlot
则根据配置的黑白名单和调用来源信息,来做黑白名单控制; -
DegradeSlot
通过统计信息针对资源的平均响应时间(RT)以及异常比率,来决定资源是否在接下来的时间被自动熔断掉; -
SystemSlot
会根据当前系统的整体情况,对入口资源的调用进行动态调配。其原理是让入口的流量和当前系统的预计容量达到一个动态平衡。
Sentinel 将 ProcessorSlot
作为 SPI 接口进行扩展(1.7.2 版本以前 SlotChainBuilder
作为 SPI),使得 Slot Chain 具备了扩展的能力。您可以自行加入自定义的 slot 并编排 slot 间的顺序,从而可以给 Sentinel 添加自定义的功能。
三、基本使用
3.1 简介
Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果。
使用 Sentinel 来进行资源保护,主要分为几个步骤:
-
定义资源
-
定义规则
-
检验规则是否生效
3.2 定义资源
Sentinel支持多种定义资源的方式,这里以注解形式定义为例
@SentinelResource 注解
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource
注解包含以下属性:
-
value
:资源名称,必需项(不能为空) -
entryType
:entry 类型,可选项(默认为EntryType.OUT
) -
blockHandler
/blockHandlerClass
:blockHandler
对应处理BlockException
的函数名称,可选项。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 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
-
-
exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(
DegradeException
)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException
时只会进入 blockHandler
处理逻辑。若未配置 blockHandler
、fallback
和 defaultFallback
,则被限流降级时会将 BlockException
直接抛出。
Spring AOP
若应用使用了 Spring AOP,需要通过配置的方式将 SentinelResourceAspect
注册为一个 Spring Bean:
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
@GetMapping("/testSentinel")
@SentinelResource(value = "testSentinelGet" , blockHandler = "blockHandlerForTestSentinel")
public ResultPojo<String> testSentinelGet() {
return ResultPojo.successResultPojo("调用成功,没有被sentinel拦截");
}
// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public ResultPojo<String> blockHandlerForTestSentinel(BlockException ex) {
return ResultPojo.errorResultPojo500("调用失败,被sentinel拦截");
}
3.3 定义规则
Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。
Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则。
3.3.1 流量控制规则 (FlowRule)
流量规则的定义
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 或线程数模式 | QPS 模式 |
limitApp | 流控针对的调用来源 | default ,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流 | 直接拒绝 |
同一个资源可以同时有多个限流规则。
通过代码定义流量控制规则
理解上面规则的定义之后,可以通过调用 FlowRuleManager.loadRules()
方法来用硬编码的方式定义流量控制规则,比如:
@Configuration
public class SentinelConfig{
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
@PostConstruct
private void initRules() {
//=============================流控规则=========================
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource("testSentinelGet");
rule1.setCount(1);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
// 将控制规则载入到 Sentinel
FlowRuleManager.loadRules(rules);
}
}
3.3.2 熔断降级规则 (DegradeRule)
熔断降级规则包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
同一个资源可以同时有多个降级规则。
理解上面规则的定义之后,可以通过调用 DegradeRuleManager.loadRules()
方法来用硬编码的方式定义流量控制规则。
private static void initDegradeRule() {
//=============================流控规则=========================
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource("testSentinelPost");
rule1.setCount(1);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
// 将控制规则载入到 Sentinel
FlowRuleManager.loadRules(rules);
//=============================降级规则=========================
List<DegradeRule> degradeRules = new ArrayList<>();
DegradeRule rule2 = new DegradeRule();
rule2.setResource("testSentinelTimeOut");
rule2.setGrade(RuleConstant.DEGRADE_GRADE_RT);
rule2.setCount(3000);
rule2.setMinRequestAmount(1);
rule2.setStatIntervalMs(60*1000);
rule2.setTimeWindow(10);;
// 将控制规则载入到 Sentinel
degradeRules.add(rule2);
DegradeRuleManager.loadRules(degradeRules);
}
3.3.3 访问控制规则 (AuthorityRule)
很多时候,需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
授权规则,即黑白名单规则(AuthorityRule
)非常简单,主要有以下配置项:
-
resource
:资源名,即限流规则的作用对象 -
limitApp
:对应的黑名单/白名单,不同 origin 用,
分隔,如appA,appB
-
strategy
:限制模式,AUTHORITY_WHITE
为白名单模式,AUTHORITY_BLACK
为黑名单模式,默认为白名单模式
使用场景:只允许从网关来的请求访问service,那么流控应用中就填写网关的名称,并在网关服务中,利用网关的过滤器添加名为gateway的origin头:
spring:
application:
name: gateway
cloud:
gateway:
default-filters:
- AddRequestHeader=origin,gateway
Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的。
实例
@Configuration
public class SentinelConfig implements RequestOriginParser {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
// 1.定义RequestOriginParser接口的实现类,基于业务在接口方法中解析请求数据并返回.
@Override
public String parseOrigin(HttpServletRequest request) {
return request.getParameter("origin");
}
// 2.定义流控规则,只有来源为 appA 和 appB 的请求才可通过,
@PostConstruct
private void initRules() {
//=============================流控规则=========================
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource("testSentinelPost");
rule1.setCount(1);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rules.add(rule1);
// 将控制规则载入到 Sentinel
FlowRuleManager.loadRules(rules);
//=============================降级规则=========================
List<DegradeRule> degradeRules = new ArrayList<>();
DegradeRule rule2 = new DegradeRule();
rule2.setResource("testSentinelTimeOut");
rule2.setGrade(RuleConstant.DEGRADE_GRADE_RT);
rule2.setCount(5000);
rule2.setMinRequestAmount(1);
rule2.setStatIntervalMs(60*1000);
rule2.setTimeWindow(10);;
// 将控制规则载入到 Sentinel
degradeRules.add(rule2);
DegradeRuleManager.loadRules(degradeRules);
//=============================访问控制规则=========================
AuthorityRule rule3 = new AuthorityRule();
rule3.setResource("testSentinelGet");
rule3.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule3.setLimitApp("appA,appB");
AuthorityRuleManager.loadRules(Collections.singletonList(rule3));
}
}