上一篇文章我们已经对 Sentinel 有个简单的了解,接下来我们将讲解 Sentinel的具体使用。 Sentinel的使用分为两部分:
- Sentinel-Dashboard: 与 Hystrix-Dashboard类似,但是它的功能更加强大。除了与 hystrix-dashboard 一样提供实时监控之外,还提供了流控规则、熔断规则的在线维护等功能。
- 客户端整合:每个微服务客户端都需要整合 sentinel 的客户端封装与配置,才能将监控信息上报给 dashboard 展示以及实时的更改限流或熔断规则等。
下面我们就分两部分来看,如果使用Sentinel来实现接口限流。
部署Sentinel Dashboard
- 下载地址:Sentinel/releases 这里我采用最新版:sentinel-dashboard-1.6.2.jar
命令行启动
代码语言:javascript复制java -jar sentinel-dashboard-1.6.2.jar
sentinel-dashboard 不像Nacos的服务端那样还提供了外置配置文件,比较容易修改参数。不过没关系,由于 sentinel-dashboard是一个标准的SpringBoot应用,所以如果需要自定义端口号等配置的话,可以通过在启动参数中增加参数来调整,比如:-Dserver.port=8888
。
默认情况下,sentinel-dashboard以8080端口启动,所以可以通过访问: http://localhost:8080 来验证是否启动成功,如果一切顺利,可以看到如下界面:
注意: 只要 1.6.0 及以上的版本,才有这个简单的登录页面。默认用户名密码都是: sentinel
。对于用户登录相关的配置可在启动命令中添加下面参数来修改默认配置:
-
Dsentinel.dashboard.auth.username=sentinel
:用于指定控制台的登录用户名为 sentinel; -
Dsentinel.dashboard.auth.password=123456
:用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel -
Dserver.servlet.session.timeout=7200
:用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;
输入用户名密码登录后,会看下如下页面:
整合Sentinel
第一步: 创建 alibaba-sentinel-rate-limiting
web应用,并在的pom.xml 中引入 Spring Cloud Alibaba的Sentinel模块依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
第二步: 在Spring Cloud 应用中 application.yml
配置 sentinel dashboard 的访问地址:
spring:
cloud:
sentinel:
transport:
# 指定sentinel控制台地址
dashboard: localhost:8080
控制台相关配置项:
配置项 | 默认值 | 最小值 | 描述 |
---|---|---|---|
sentinel.dashboard.app.hideAppNoMachineMillis | 0 | 60000 | 是否隐藏无健康节点的应用,距离最近一次主机心跳时间的毫秒数,默认关闭 |
sentinel.dashboard.removeAppNoMachineMillis | 0 | 120000 | 是否自动删除无健康节点的应用,距离最近一次其下节点的心跳时间毫秒数,默认关闭 |
sentinel.dashboard.unhealthyMachineMillis | 60000 | 30000 | 主机失联判定,不可关闭 |
sentinel.dashboard.autoRemoveMachineMillis | 0 | 300000 | 距离最近心跳时间超过指定时间是否自动删除失联节点,默认关闭 |
server.port | 8080 | - | 指定端口 |
csp.sentinel.dashboard.server | localhost:8080 | - | 指定地址 |
project.name | - | - | 指定程序的名称 |
sentinel.dashboard.auth.username [1.6版本支持] | sentinel | - | Sentinel Dashboard登录账号 |
sentinel.dashboard.auth.password [1.6版本支持] | sentinel | - | Sentinel Dashboard登录密码 |
server.servlet.session.timeout [1.6版本支持] | 30分钟 | - | 登录Session过期时间。配置为7200表示7200秒;配置为60m表示60分钟 |
控制台配置项需在启动命令中指定,例如指定账户密码,如下:
代码语言:javascript复制java -jar -Dsentinel.dashboard.auth.username=admin -Dsentinel.dashboard.auth.password=123456 sentinel-dashboard-1.6.3.jar
第三步: 创建应用rest接口:
代码语言:javascript复制@Slf4j
@RestController
public class UserController {
@GetMapping("/findById")
public String getUser(@RequestParam String id) {
return "hello sentinel dashboard";
}
}
第四步: 启动应用,然后通过 postman 访问:http://localhost:9005/findById接口。
sentinel 是懒加载应用的,所有这里需要通过postman先访问,才能在控制台看到应用客户端。
此时,在Sentinel Dashboard界面中就可以看到我们启动的这个服务以及接口调用的实时监控了。
实时监控
控制台配置规则
配置限流规则
在完成了上面配置以后,我们在 Sentinel 控制台的 alibaba-sentinel-rate-limiting
服务下,单击 簇点链路 菜单,可以看到如下页面:
点击 流控按钮,便可以为应用设置流控规则
image.png
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单机阈值:
- QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
- 线程数:当调用该api的线程数达到阈值的时候,进行限流
- 是否集群:不需要集群,暂不研究
- 流控模式:
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
链路模式稍微有些抽象,这里举个简单的例子说明一下。下图中有两个调用链路,图中的/test-b和/test-a实际就是两个接口,它们都调用了同一个common资源,所以/test-b和/test-a就称为common的入口资源:
此时我为common添加一个限流规则如下:
可以看到流控模式选择链路后,需要填写一个入口资源,我这里填的是/test-a,那么这意味着什么呢?意味着当/test-a的QPS达到该规则的阈值后,就会对/test-a限流,同时/test-b不会受到任何影响。说明这种流控模式可以针对接口级别的来源进行限流,而“针对来源”则是对微服务级别的来源进行限流。
- 流控效果:
- 快速失败:直接失败,抛出异常,不做任何额外的处理,是最简单的效果
相关源码:
com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
- Warm Up(预热):会根据codeFactor(默认3)的值,从阈值除以codeFactor,经过预热时长,才到达设置的QPS阈值。适用于将突然增大的流量转换为缓步增长的场景。
相关的官方文档:限流 - 冷启动
相关源码:
com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
- 排队等待:匀速排队,让请求以均匀的速度通过,若请求等待时间超过设置的超时时间则抛弃该请求,阈值类型必须设置成QPS,否则无效。适用于突发流量的场景。
相关的官方文档:限流 - 匀速器
相关源码:
com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
- 快速失败:直接失败,抛出异常,不做任何额外的处理,是最简单的效果
相关源码:
降级规则(断路器模式)
点击降级按钮,便可以为应用设置降级规则
降级策略:
- RT:平均响应时间(秒级统计)超出阈值 且 在时间窗口内的请求 >= 5时,触发降级(断路器打开);时间窗口结束后,关闭降级【Sentinel默认最大的RT为4900ms,可以通过
-Dcsp.sentinel.statistic.max.rt=xxx
修改】 - 异常比例:QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
- 异常数:异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级【时间窗口 < 60秒可能会出现问题】
关于异常数这种降级策略需要注意的点:
若将时间窗口的值设置小于60秒则可能会出问题,因为异常数的统计是分钟级别的,时间窗口小于60秒就有可能不断进入降级状态.
降级规则的相关源码:
com.alibaba.csp.sentinel.slots.block.degradeDegradeRule#passCheck
(对降级的判断都在这个方法里完成)
热点规则(热点参数限流规则)
Sentinel默认显示的端点并不支持热点规则,要显示热点规则,需要自己添加代码:
代码语言:javascript复制@GetMapping("test")
@SentinelResource("test")
public String testHot(@RequestParam(required = false) String a,
@RequestParam(required = false) String b) {
return a "-" b;
}
点击热点按钮,便可以为test设置热点规则
在时间窗口以内,一旦该api指定索引的参数QPS达到了域名,就会触发限流
- 参数索引:从0开始,上面的代码中:a的参数索引为0;b的参数索引为1【参数索引对应的参数必须时基本类型或者String】
热点规则适用的场景:
- 适用于存在热点参数并希望提升API可用性的场景,即某个特定请求参数QPS偏高于其他请求参数时,仅对该参数的请求限流,使用其他请求参数则可以正常响应,这样可以提高一定的可用性
使用热点规则需要注意的点:
- 参数必须是基本类型或者String类型,否则将不会生效
热点规则相关源码: com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker#passCheck(对热点参数规则的判断逻辑都在这个方法里)
系统规则
阈值类型
- LOAD(
仅对 Linux/Unix-like 机器生效
):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是CPU cores * 2.5
- RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒
- 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护
- CPU 使用率:当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)
授权规则
点击授权按钮,便可以为应用设置授权规则
资源名所代表的资源只允许流控应用中添加的微服务使用(白名单)、不允许使用(黑名单)
代码配置规则
流控规则
参数
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | 无 |
count | 限流阈值 | 无 |
grade | 限流阈值类型,QPS 或线程数模式 | QPS模式 |
limitApp | 流控针对的调用来源 | default,代表不区分调用来源 |
strategy | default,代表不区分调用来源 | 根据资源本身 |
controlBehavior | 流控效果(直接拒绝 / 排队等待 / 慢启动模式) | 直接拒绝 |
代码
代码语言:javascript复制private void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule(resourceName);
// 设置QPS阈值为20
rule.setCount(20);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
降级规则
参数
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即限流规则的作用对象 | 无 |
count | 阈值 | 无 |
grade | 降级模式,根据 RT 降级还是根据异常比例降级 | RT |
timeWindow | 降级的时间,单位为 s | 无 |
代码
代码语言:javascript复制private void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource(KEY);
// set threshold RT, 10 ms
rule.setCount(10);
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
热点规则
参数
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,必填 | 无 |
count | 限流阈值,必填 | 无 |
grade | 限流模式 | QPS 模式 |
durationInSec | 统计窗口时间长度(单位为秒) | 1s |
controlBehavior | 流控效果(支持快速失败和匀速排队模式) | 快速失败 |
maxQueueingTimeMs | 最大排队等待时长(仅在匀速排队模式生效) | 0ms |
paramIdx | 热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置 | 无 |
paramFlowItemList | 参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型 | 无 |
clusterMode | 是否是集群参数流控规则 | false |
clusterConfig | 集群流控相关配置 | 无 |
代码
代码语言:javascript复制ParamFlowRule rule = new ParamFlowRule(resourceName)
.setParamIdx(0)
.setCount(5);
// 针对 int 类型的参数 PARAM_B,单独设置限流 QPS 阈值为 10,而不是全局的阈值 5.
ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf(PARAM_B))
.setClassType(int.class.getName())
.setCount(10);
rule.setParamFlowItemList(Collections.singletonList(item));
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
系统规则
参数
Field | 说明 | 默认值 |
---|---|---|
highestSystemLoad | 最大的 load1 | -1(不生效) |
avgRt | 所有入口流量的平均响应时间 | -1(不生效) |
maxThread | 入口流量的最大并发数 | -1(不生效) |
qpa | 所有入口资源的 QPS | -1(不生效) |
代码
代码语言:javascript复制private void initSystemRule() {
List<SystemRule> rules = new ArrayList<>();
SystemRule rule = new SystemRule();
rule.setHighestSystemLoad(10);
rules.add(rule);
SystemRuleManager.loadRules(rules);
}
授权规则
参数
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即限流规则的作用对象 | 无 |
limitApp | 对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB | default,代表不区分调用来源 |
strategy | 限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式 | AUTHORITY_WHITE |
代码
代码语言:javascript复制AuthorityRule rule = new AuthorityRule();
rule.setResource("test");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("appA,appB");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));
Sentinel组件与控制台通信原理
- 注册/心跳发送 源码:com.alibaba.csp.sentinel.transport.heartbeat.SimpleHttpHeatbeatSender
- 通信API 源码:com.alibaba.csp.sentinel.command.CommandHandler