Sentinel 笔记

1,273 阅读14分钟

  • 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 等,当到达配置阈值触发负载保护。

定义资源

资源,可以是任何东西,服务、服务里的方法、一段代码。

先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。

方式一:主流框架的默认适配

github.com/alibaba/Sen…

方式二:抛出异常定义资源

// 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
  1. 1532415661000:时间戳
  2. 2018-07-24 15:01:01:格式化之后的时间戳
  3. sayHello(java.lang.String):资源名
  4. 12:表示到来的数量,即此刻通过 Sentinel 规则 check 的数量(passed QPS)
  5. 3:实际该资源被拦截的数量(blocked QPS)
  6. 4:每秒结束的资源个数(完成调用),包括正常结束和异常结束的情况(exit QPS)
  7. 2:异常的数量
  8. 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 参数

同时,需要修改 ApolloConfigFlowRuleApolloProviderFlowRuleApolloPublisher 类中的 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:

参考