- spring-boot:2.1.3.RELEASE
- JDK:1.8
- sentinel:1.7.0
快速入门(注解)
加入依赖
<!-- sentinel 核心 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.7.0</version>
</dependency>
<!-- 注解支持 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
应用代码中添加标识资源注解,且注册规则,如:
@SentinelResource(value = "/interface1", blockHandler = "blockHandlerFor1")
@RequestMapping("/interface1")
@ResponseBody
public String interface1(String id) {
return "interface1";
}
// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public String blockHandlerFor1(String id, BlockException ex) {
return "interface1 block";
}
// 注册规则
// 此处使用 流控规则,注意区别
static {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("/interface1");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(20); // Set limit QPS to 20.
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
增加 sentinel 拦截配置:
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
若不自定义资源名,资源名默认:完整包名+方法签名,如:
com.ariclee.springboot.sentinel.TestController:interface1(java.lang.String)
工作机制、概念
资源 + 规则:在代码中定义资源,在配置中心或者代码中配置规则。
-
对主流框架提供适配或者显示的 api,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
-
根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
-
Sentinel 提供实时监控系统,方便快速了解目前系统的状态
-
流量控制(flow control、Controller)
流量控制用于调整网络包的发送数据。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的,需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
流量控制有以下几个角度
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系
- 运行指标。例如 QPS、线程池、系统负载等
- 控制的效果,例如直接限流、冷启动、排队等
在系统入口出做限流,限制某接口对外提供的请求并发数目。
-
熔断降级(Service、Manager)
由于调用关系复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。
当检测到调用链路中的某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高时,则对此资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联故障。
系统内调用了其他服务(dubbo、http等),其他服务不稳定肯能拖垮本系统,为解决此问题,将调用链路及时熔断,避免级联故障。
-
系统负载保护(System)
系统维度的自适应保护能力。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
如 linux 的 load 参数、CPU useage 等,当到达配置阈值触发负载保护。
定义资源
资源,可以是任何东西,服务、服务里的方法、一段代码。
先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。
方式一:主流框架的默认适配
方式二:抛出异常定义资源
// JDK1.5 语法,try-with-resources
try (Entry entry = SphU.entry("resourceName")) {
// 被保护的业务逻辑
}
catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 在此处进行相应的处理操作
}
方式三:返回布尔值方式定义资源
if (SphO.entry("resourceName")) {
try {
// 被保护的业务逻辑
}
finally { // 保证finally会被执行
SphO.exit();
}
}
else {
}
方式四:注解
使用注解需要额外在 pom 中引入:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>x.y.z</version>
</dependency>
还需要在代码中配置 Sentinel 拦截器
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
如此就可以在代码中使用注解进行声明资源了
// 原本的业务方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public User blockHandlerForGetUser(String id, BlockException ex) {
return new User("admin");
}
注解属性:
- value:资源名,必填
- entryType:默认为 EntryType.OUT,可选
- blockHandler、blockHandlerClass:
blockHandler 对应处理 BlockExeception 的函数名称。可选项。 blockHandler 函数默认需要与被保护方法在同一个类中,访问权限应为 public,返回类型需要与被保护方法一致,参数类型需要和原方法匹配,必须在参数列表最后加一个 BlockException 类型的参数。若想使用其他类的方法,则可以使用 blockHandlerClass 指定类型,方法必须为 static。
- fallback、fallbackClass:
fallback 函数名称,可选。用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求与 blockHandler 参数一致,但可以额外多一个 Throwable 类型的参数用于接收对应的异常。
- defaultFallback:
默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名和位置要求与 fallback 参数一致,但可以额外多一个 Throwable 类型的参数用于接收对应的异常。
注: 配置 fallback 属性,不配置 blockHandler 属性,当被限流而抛出的异常也能被处理,能进入 fallback 所配置的方法中。不配置 fallback 属性,配置 blockHandler 属性,被降级导致的异常,直接抛出,不进入 blockHandler 配置的方法中。
定义规则
Sentinel 的所有规则都支持在在内存态中动态地查询及修改,修改之后立即生效(使用 Sentinel 控制台)。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略(硬编码)。
- 代码硬编码
// 定义流控规则并注册
private void initFlowRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("/interface1");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(20); // Set limit QPS to 20.
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
// 定义降级规则并注册
private void initDegradeRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("/interface1");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(20); // Set limit QPS to 20.
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
// 同理可得 SystemRule、SystemRuleManager
- 通过控制台、http 接口动态配置 进入控制台首页,从簇点链路中选择需要流控、降级的节点,如下:
点击流控:
点击降级
规则分类
Sentinel 支持以下几种规则:
- 流量控制规则
- 熔断降级规则
- 系统保护规则
- 来源访问控制规则
- 热点参数规则
流量控制规则(FlowRule)
字段名 | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 |
limitApp | 流控针对的调用来源 | default,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流 | 直接拒绝 |
同一个资源可以定义多个规则。
熔断降级规则(DegradeRule)
字段名 | 说明 | 默认值 |
---|---|---|
resource | 资源名,即限流规则的作用对象 | |
count | 阈值 | |
grade | 熔断策略,支持秒级 RT/秒级异常比例/分钟级异常数 | 秒级平均 RT |
timeWindow | 降级的时间,单位为 s | |
rtSlowRequestAmount | RT 模式下 1 秒内连续多少个请求的平均 RT 超出阈值方可触发熔断(1.7.0 引入) | 5 |
minRequestAmount | 异常熔断的触发最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
grade 参数值可在类 RuleConstant 中看到
- DEGRADE_GRADE_RT:RT,平均响应时间
当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。
- DEGRADE_GRADE_EXCEPTION_RATIO:秒级异常比例
当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
- DEGRADE_GRADE_EXCEPTION_COUNT:分钟级异常数
当资源近 1 分钟的异常数目超过阈值(count)之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。
同一个资源可以定义多个规则。
系统保护规则
目前没遇到,有需要参考官网
来源访问控制规则
目前没遇到,有需要参考官网
热点参数规则
目前没遇到,有需要参考官网
控制台(dashboard)
jar 包下载链接:github.com/alibaba/Sen…
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
客户端接入控制台:
- 客户端工程引入 jar
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.7.0</version>
</dependency>
- 配置应用启动参数:
## consoleIp、port 为控制台 ip 和端口
-Dcsp.sentinel.dashboard.server=consoleIp:port
启动所有参数
配置项 | 类型 | 默认值 | 最小值 | 描述 |
---|---|---|---|---|
auth.enabled boolean | true | - | 是否开启登录鉴权,仅用于日常测试,生产上不建议关闭 | |
sentinel.dashboard.auth.username | String | sentinel | - | 登录控制台的用户名,默认为 sentinel |
sentinel.dashboard.auth.password | String | sentinel | - | 登录控制台的密码,默认为 sentinel |
sentinel.dashboard.app.hideAppNoMachineMillis | Integer | 0 | 60000 | 是否隐藏无健康节点的应用,距离最近一次主机心跳时间的毫秒数,默认关闭 |
sentinel.dashboard.removeAppNoMachineMillis | Integer | 0 | 120000 | 是否自动删除无健康节点的应用,距离最近一次其下节点的心跳时间毫秒数,默认关闭 |
sentinel.dashboard.unhealthyMachineMillis | Integer | 60000 | 30000 | 主机失联判定,不可关闭 |
sentinel.dashboard.autoRemoveMachineMillis | Integer | 0 | 300000 | 距离最近心跳时间超过指定时间是否自动删除失联节点,默认关闭 |
!!注!! 如果使用的 java -jar 启动应用程序,切记将参数跟在 -jar 后面,如下:
java -jar -Dcsp.sentinel.dashboard.server=consoleIp:port -Dproject.name=sentinel-test-app2 sentinel-tutorital-0.0.1.jar
否则,程序会无法读到参数
启动控制台(已有客户端连接),进入主页,默认账号密码:sentinel/sentinel
与客户端(应用)相关
HTTP 接口
引入 transport 模块后,提供了一些 http api,如下:
- 通过 sentinel 暴露的 http 接口,访问实时统计信息
# curl http://localhost:8719/cnode?id=resourceName
输出如下:
idx id thread pass blocked success total aRt 1m-pass 1m-block 1m-all exception
2 /interface1 0 14.0 0.0 14.0 14.0 0.0 997 0 997 0.0
字段含义:
属性 | 解释 |
---|---|
thread | 当前处理该资源的线程数 |
pass | 一秒内到来到的请求 |
blocked | 一秒内被流量控制的请求数量 |
success | 一秒内成功处理完的请求 |
total | 到一秒内到来的请求以及被阻止的请求总和 |
RT | 一秒内该资源的平均响应时间 |
1m-pass | 一分钟内到来的请求 |
1m-block | 一分钟内被阻止的请求 |
1m-all | 一分钟内到来的请求和被阻止的请求的总和 |
exception | 一秒内业务本身异常的总和 |
- 获取已加载的规则
# 若 type=flow 则返回限流规则列表,若为 degrade 返回降级规则列表
# system 则返回系统保护规则
# curl http://localhost:8719/getRules?type=<XXXX>
# 获取所有热点规则
# curl http://localhost:8719/getParamRules
日志
- 拦截日志详情:
${user_home}/logs/csp/sentinel-block.log
- 秒级监控日志:
${user_home}/logs/csp/${app_name}-${pid}-metrics.log
格式如下:
1532415661000|2018-07-24 15:01:01|sayHello(java.lang.String)|12|3|4|2|295
1532415661000
:时间戳2018-07-24 15:01:01
:格式化之后的时间戳sayHello(java.lang.String)
:资源名12
:表示到来的数量,即此刻通过 Sentinel 规则 check 的数量(passed QPS)3
:实际该资源被拦截的数量(blocked QPS)4
:每秒结束的资源个数(完成调用),包括正常结束和异常结束的情况(exit QPS)2
:异常的数量295
:资源的平均响应时间(RT)
- 业务日志:
${user_home}/logs/csp/sentinel-record.log.xxx
动态规则扩展
通过控制台修改的规则,存在于客户端的内存中,在客户端应用重启后会丢失。
建议通过控制台设置规则后将规则推送到统一的规则中心,客户端实现
ReadableDataSource
接口端监听规则中心实时获取变更,流程如下:
-
与 Apollo 整合
客户端
引入适配依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
<version>1.7.0</version>
</dependency>
注册监听(启动时需要执行以下代码)
// Set up basic information, only for demo purpose. You may adjust them based on your actual environment.
// For more information, please refer https://github.com/ctripcorp/apollo
String namespaceName = "TEST1.test";
String flowRuleKey = "flowRules";
String appId = "test";
String apolloMetaServerAddress = "http://172.18.12.52:12000";
System.setProperty("app.id", appId);
System.setProperty("apollo.meta", apolloMetaServerAddress);
// It's better to provide a meaningful default value.
String defaultFlowRules = "[{}]";
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName,
flowRuleKey, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
String degradeRuleKey = "degradeRules";
ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new ApolloDataSource<>(namespaceName,
degradeRuleKey, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {
}));
DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());
Sentinel Dashboard
- 后端代码修改
dashboard 项目提供了 Nacos、ZooKeeper 和 Apollo 的推送和拉取规则实现示例,在 test
目录下,需要将所需的类移动到 src/main 下。
FlowControllerV2
类中指定对应的 bean 即可开启 Apollo 适配,如下:
@Autowired
@Qualifier("flowRuleApolloProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> apolloRuleProvider;
@Autowired
@Qualifier("flowRuleApolloPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> apolloRulePublisher;
调整 apollo 参数
同时,需要修改 ApolloConfig
、FlowRuleApolloProvider
、FlowRuleApolloPublisher
类中的 apollo 配置项。如下:
@Configuration
public class ApolloConfig {
// ... 略
public static String appId = "test";
public static String namespace = "TEST1.test";
@Bean
public ApolloOpenApiClient apolloOpenApiClient() {
ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()
// 这里填的是 apollo 配置页面的地址
// 不是 注册中心 地址
.withPortalUrl("http://172.18.12.52:10000")
// 授权码,详见 apollo 官方文档
.withToken("23d478d85eb2f4e59fb0b82e46dc59187f7417c9")
.build();
return client;
}
}
设置正确的提交人
FlowRuleApolloPublisher#publish 方法中,用到 setDataChangeCreatedBy、setReleasedBy 方法填写提交人用户名,必须要正确填写,否则报错
user(dataChangeLastModifiedBy) not exists
改为 dto.setReleasedBy|setDataChangeCreatedBy("apollo") 即可。
同时支持推送客户端和推送配置中心
FlowControllerV2#publishRules
方法默认不会将配置发布到客户端,所以即使在页面上新增或者修改了配置,客户端无法感知,所以需要仿照 FlowControllerV1#publishRules
方法加上 sentinelApiClient.setFlowRuleOfMachine
操作。
- 前端代码修改
dashboard 项目也提供了配套的 v2 版本代码,只需修改前端代码的路由即可:
-
修改侧边栏前端路由配置:
sidebar.html
流控规则路由从dashboard.flowV1
改成dashboard.flow
即可 -
簇点链路页面对话框提交按钮路由:修改
identity.js
文件,查找 FlowServiceV1 关键字,替换为 FlowServiceV2,替换后就会加载flow_service_v2.js
中方法 -
与 Zookeeper 整合
TODO
-
与 Nacos 整合
TODO
项目启发
TODO: