Sentinel 基本认识
Sentinel 是一种面向分布式微服务架构的轻量级高可用流量控制组件,由阿里巴巴中间件团队开发的开源项目。Sentinel 主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。详细参考 官网详解 Sentinel 主要由以下两个部分组成:- Sentinel 核心库:Sentinel 的核心库不依赖任何框架或库,能够运行于 Java 8 及以上的版本的运行时环境中,同时对 Spring Cloud、Dubbo 等微服务框架提供了很好的支持。
- Sentinel 控制台(Dashboard):Sentinel 提供的一个轻量级的开源控制台,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。
Sentinel 核心库不依赖 Sentinel Dashboard,但两者结合使用可以有效的提高效率,让 Sentinel 发挥它最大的作用。Sentinel 的基本概念有两个,资源 和 规则。
基本概念 | 描述 |
---|---|
资源 | 资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如由应用程序提供的服务或者是服务里的方法,甚至可以是一段代码。 我们可以通过 Sentinel 提供的 API 来定义一个资源,使其能够被 Sentinel 保护起来。通常情况下,我们可以使用方法名、URL 甚至是服务名来作为资源名来描述某个资源。 |
规则 | 围绕资源而设定的规则。Sentinel 支持流量控制、熔断降级、系统保护、来源访问控制和热点参数等多种规则,所有这些规则都可以动态实时调整。 |
Sentinel 控制台提供的功能如下:
- 查看机器列表以及健康情况:Sentinel 控制台能够收集 Sentinel 客户端发送的心跳包,判断机器是否在线。
- 监控(单机和集群聚合):Sentinel 控制台通过 Sentinel 客户端暴露的监控 API,可以实现秒级的实时监控。
- 规则管理和推送:通过 Sentinel 控制台,我们还能够针对资源定义和推送规则。
- 鉴权:从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel。
安装 Sentinel Dashboard
- 下载Sentinel Dashboard
<font style="color:rgb(68, 68, 68);">jar</font>
地址入口Releases · alibaba/Sentinel
- 打开命令行窗口,跳转 Sentinel Dashboard jar 包所在的目录,执行以下命令,启动 Sentinel Dashboard
java -jar sentinel-dashboard-1.8.5.jar
- 访问 http://localhost:8080/ 默认账号/密码:sentinel/sentinel
Sentinel 使用步骤方法
- 新建 Module
spring-cloud-alibaba-sentinel-8501
- 导入 Sentinel依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--父工程-->
<parent>
<groupId>com.chen</groupId>
<artifactId>spring-cloud-alibaba-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.chen</groupId>
<artifactId>spring-cloud-alibaba-sentinel-8501</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-alibaba-sentinel-8501</name>
<description>spring-cloud-alibaba-sentinel-8501</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yaml
配置
server:
port: 8501
spring:
application:
name: chen-sentinel-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: 127.0.0.1:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: 127.0.0.1:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8917
management:
endpoints:
web:
exposure:
include: '*'
- 主启动类添加
<font style="color:#9e880d;">@EnableDiscoveryClient</font>
package com.chen.springcloudalibabasentinel8501;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudAlibabaSentinel8501Application {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaSentinel8501Application.class, args);
}
}
- 业务类
package com.chen.springcloudalibabasentinel8501.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FlowLimitController {
@Value("${server.port}")
private String serverPort;
@GetMapping("/testA")
public String testA() {
return "serverPort:[" + serverPort + "],testA……";
}
@GetMapping("/testB")
public String testB() {
return "serverPort:[" + serverPort + "],testB……";
}
}
- Sentinel采用懒加载机制。所以请求 http://localhost:8501/testB后,Sentinel 控制台 首页 下方新增了一个
chen-sentinel-service
的菜单,而这正是spring-cloud-alibaba-sentinel-8501
的服务名(spring.application.name),说明 Sentinel 已经监控到这个服务,如下图。
实时监控
可以实时监测我们的服务请求次数,我们多次刷新请求页面,这里都可以记录下来
定义资源
资源是 Sentinel 中的核心概念之一。在项目开发时,我们只需要考虑这个服务、方法或代码是否需要保护,如果需要保护,就可以将它定义为一个资源。Sentinel 为我们提供了多种定义资源的方式:- 适配主流框架自动定义资源
- 通过 SphU 手动定义资源
- 通过 SphO 手动定义资源
- 注解方式定义资源
适配主流框架自动定义资源
Sentinel 对大部分的主流框架都进行了适配,我们只要引入相关的适配模块(<font style="color:rgb(68, 68, 68);">spring-cloud-starter-alibaba-sentinel</font>
),Snetinel 就会自动将项目中的服务(包括调用端和服务端)定义为资源,资源名就是服务的请求路径。此时,我们只要再定义一些规则,这些资源就可以享受到 Sentinel 的保护。
我们可以在 Sentinel 控制台的“簇点链路”中,直接查看被 Sentinel 监控的资源,如下图:
通过 SphU 手动定义资源
Sentinel 提供了一个名为 SphU 的类,它包含的 try-catch 风格的 API ,可以帮助我们手动定义资源。使用步骤如下:
- 在SentinelFlowLimitController 中新增一个
<font style="color:#00627a;">testCbySphU</font>()
方法定义一个名为<font style="color:#00627a;">testCbySphU</font>
的资源,代码如下
@GetMapping("/testC")
public String testC() {
return testCbySphU();
}
/**
* 通过 SphU 手动定义资源
*/
public String testCbySphU() {
Entry entry = null;
try {
entry = SphU.entry("testAbySphU");
String str = "testCbySphU端口[" + serverPort + "]请求成功";
System.out.println(str);
return str;
} catch (BlockException e) {
String str = "testCbySphU端口[" + serverPort + "]请求被限流";
System.out.println(str);
return str;
} finally {
if (entry != null) {
entry.exit();
}
}
}
- 重启项目,浏览器访问 http://localhost:8501/testC
通过 SphO 手动定义资源
Sentinel 还提供了一个名为 SphO 的类,它包含了 if-else 风格的 API,能帮助我们手动定义资源。通过这种方式定义的资源,发生了限流之后会返回 false,此时我们可以根据返回值,进行限流之后的逻辑处理。- 在SentinelFlowLimitController 中新增一个
<font style="color:#00627a;">testDbySphO</font>()
方法定义一个名为 testDbySphO的资源,代码如下
@GetMapping("/testD")
public String testD() {
return testDbySphO();
}
/**
* 通过 SphO 手动定义资源
*/
public String testDbySphO() {
if (SphO.entry("testDbySphO")) {
// 务必保证finally会被执行
try {
String str = "testDbySphU端口[" + serverPort + "]请求成功";
return str;
} finally {
SphO.exit();
}
} else {
// 资源访问阻止,被限流或被降级
//流控逻辑处理 - 开始
String str = "testDbySphU端口[" + serverPort + "]请求被限流";
return str;
//流控逻辑处理 - 结束
}
}
- 重启项目,浏览器访问 http://localhost:8501/testD
注解方式定义资源(推荐)
除了以上两种方式外,我们还可以通过 Sentinel 提供的 @SentinelResource 注解定义资源,步骤如下:- 在SentinelFlowLimitController 中新增一个
<font style="color:#00627a;">testEbyAnnotation</font>()
方法定义一个名为 testEbyAnnotation的资源,代码如下
@GetMapping("/testE")
@SentinelResource("testEbyAnnotation")
public String testE() {
return testEbyAnnotation();
}
public String testEbyAnnotation(){
String str = "testEbySphU端口[" + serverPort + "]请求成功";
return str;
}
- 重启项目,浏览器访问 http://localhost:8501/testE
Sentinel 流量控制
- QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流。线程数:当调用该api的线程数达到阈值的时候,进行限流
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
- 快速失败:直接失败,抛异常
- Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设 置的QPS阈值
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
流量控制简单使用
任何系统处理请求的能力都是有限的,但任意时间内到达系统的请求量往往是随机且不可控的,如果在某一个瞬时时刻请求量急剧增,那么系统就很有可能被瞬时的流量高峰冲垮。为了避免此类情况发生,我们都需要根据系统的处理能力对请求流量进行控制,这就是我们常说的 流量控制,简称 流控。 Sentinel 作为一种轻量级高可用流量控制组件,流量控制是它最主要的工作之一。 我们可以针对资源定义流控规则,Sentinel 会根据这些规则对流量相关的各项指标进行监控。指标当达到或超过流控规则规定的阈值时,Sentinel 会对请求的流量进行限制(即限流),以避免系统被瞬时的流量高峰冲垮,保障系统的高可用性。 一条流量规则主要由下表中的属性组成,我们可以通过组合这些属性来实现不同的限流效果。属性 | 说明 | 默认值 |
---|---|---|
资源名(resource) | 流控规则的作用对象。 | |
阈值(count) | 流控的阈值。 | |
阈值类型(grade) | 流控阈值的类型,包括 QPS 或并发线程数。 | QPS |
针对来源(limitApp) | 流控针对的调用来源。 | default,表示不区分调用来源 |
流控模式(strategy) | 调用关系限流策略,包括直接、链路和关联。 | 直接 |
流控效果(controlBehavior) | 流控效果(直接拒绝、Warm Up、匀速排队),不支持按调用关系限流。 | 直接拒绝 |
QPS 表示并发请求数,即每秒钟最多通过的请求数。同一个资源可以创建多条流控规则,Sentinel 会遍历这些规则,直到有规则触发限流或者所有规则遍历完毕为止。
Sentinel 触发限流时,资源会抛出 BlockException 异常,此时我们可以捕捉 BlockException 来自定义被限流之后的处理逻辑。
Sentinel 控制台定义流控规则
- SentinelFlowLimitController 中新增一个名为 testF 的服务方法如下:
@GetMapping("/testF")
public String testF() {
return "serverPort:[" + serverPort + "],testF……";
}
- 重启服务,访问 http://localhost:8501/testF
- 到控制台对资源设置规则
<font style="color:rgb(68, 68, 68);">/testF</font>
右侧的 +流控 按钮,在弹出的“新增流控规则”窗口中定义流控规则
- 快速连续(频率大于每秒钟 2 次)访问 http://localhost:8501/testF,结果如下


java
@GetMapping("/testF")
@SentinelResource(value = "testF-resource",blockHandler = "blockHandlerTestF")
public String testF() {
return "serverPort:[" + serverPort + "],testF……";
}
public String blockHandlerTestF(BlockException exception){
String str = Thread.currentThread().getName() + "端口[" + serverPort + "]请求被限流,请稍后重试";
return str;
}
在以上代码中,我们通过 @SentinelResource 注解的 blockHandler 属性指定了一个 blockHandler 函数,进行限流之后的后续处理。使用 @SentinelResource 注解的 blockHandler 属性时,需要注意以下事项:
- blockHandler 函数访问范围需要是 public;
- 返回类型需要与原方法相匹配;
- 参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException;
- blockHandler 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 blockHandler 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
- 请务必添加 blockHandler 属性来指定自定义的限流处理方法,若不指定,则会跳转到错误页(用户体验不好)。
- 在Sentinel 控制台主页,点击 sentinel-sevice 下的 簇点链路,点击资源
<font style="color:rgb(68, 68, 68);">testD-resource</font>
右侧的 +流控 按钮,并在弹出的“新增流控规则”窗口中为这个资源定义流控规则,流控规则内容为 :QPS 的阈值为 2 - 快速连续(频率大于每秒钟 2 次)访问 http://localhost:8501/testF 结果如下:
通过代码定义流量控制规则
还可以在服务代码中,调用<font style="color:rgb(36, 41, 46);">FlowRuleManager.loadRules()</font>
方法来定义流控规则,该方法需要一个 FlowRule 类型的 List 集合作为其参数,示例代码如下。
https://www.yuque.com/chenlaohan/xnax4v/akgnzc?inner=eMoQ1
- 定义一个
<font style="color:rgb(68, 68, 68);">initFlowRules() </font>
方法,为名为<font style="color:rgb(68, 68, 68);">testD-resource</font>
的资源定义流控规则:每秒最多只能通过 2 个请求,即 QPS 的阈值为 2。在 testF() 方法中调用 initFlowRules() 方法,初始化流控规则
@GetMapping("/testF")
@SentinelResource(value = "testF-resource",blockHandler = "blockHandlerTestF")
public String testF() {
initFlowQpsRule();
return "serverPort:[" + serverPort + "],testF……";
}
public String blockHandlerTestF(BlockException exception){
String str = Thread.currentThread().getName() + "端口[" + serverPort + "]请求被限流,请稍后重试";
return str;
}
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
//定义一个限流规则对象
FlowRule rule1 = new FlowRule();
//资源名称
rule1.setResource("testF-resource");
//设置QPS的阈值为2
rule1.setCount(2);
//限流阈值的类型
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
- 浏览器访问 http://localhost:8501/testF,每秒访问超过2次
流控模式
流控模式有一下三种情况
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
直接(默认)+ 快速失败(默认)
QPS直接快速失败
QPS:query per second,每秒钟的请求数量,当调用该api的QPS达到阈值时,进行限流。
下面设置表示每秒请求两次或以内是OK,若QPS>2,就直接-快速失败,报默认错误
线程数直接失败
当调用该api的线程数达到阈值的时候,进行限流和QPS类似。
与QPS直接快速失败不同的是,QPS情况下限制的是流量,比如银行的人流量只能是1人/s,也就是说每次只能一个人进入银行办理业务;而线程数就好比银行只有一个窗口开放,一群人都可以进入银行,但是每次只能处理一个人的业务。
在请求中给线程休眠 0.9秒
@GetMapping("/testG")
public String testG() {
try {
Thread.sleep(9000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "serverPort:[" + serverPort + "],testG……";
}
多次请求,创建多个线程。这里直接报错
关联
当关联的资源达到阈值时,就限流自己。比如当与A关联的资源B达到阈值后,就限流A自己。使用如下:
创建两个请求资源
@GetMapping("/testG")
public String testG() {
return "serverPort:[" + serverPort + "],testG……";
}
@GetMapping("/testH")
public String testH() {
return "serverPort:[" + serverPort + "],testH……";
}
添加流控规则。当请求 资源/testH
超过阈值时,限流资源 /testG
资源请求
Postman
模拟并发密集访问testG
。先创建一个集合,名字自己随便取。
新建一个组,创建一个请求,请求我们的 http://localhost:8501/testH。选中组创建并发请求
请求过程中会发现在请求 http://localhost:8501/testG 就限流了
链路
作用:只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。
Sentinel从1.6.3版本开始,Sentinel Web Filter 默认收敛所有的URL入口的Context,因此链路限流不生效 1.7.0版本开始,官方在CommomFilter中引入了WEB_CONTEXT_UNIFY
这个init parameter,用于控制是否收敛context,将其配置为false
即可根据不同的URL进行链路限流
Spring Cloud Alibaba 在2.1.1.RELEASE版本后,可以通过配置spring.cloud.sentinel.web-context-unify=false关闭
java
@GetMapping("/testG")
public String testG() {
String s = flowLimitService.testLink(serverPort);
return "get /testG 【"+s+"】";
}
@GetMapping("/testH")
public String testH() {
String s = flowLimitService.testLink(serverPort);
return "get /testH 【"+s+"】";
}
java
package com.chen.springcloudalibabasentinel8501.service;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
@Service
public class FlowLimitService {
@SentinelResource(value = "testLink",blockHandler = "catchTestLink")
public String testLink(String serverPort) {
String str = "serverPort:[" + serverPort + "],testLink……";
return str;
}
public String catchTestLink(@PathVariable("serverPort") String serverPort, BlockException exception){
return "testLink限流成功";
}
}
2. 流控资源规则添加


- 关联 :假设A规则关联B,那么A资源是受限制的
- 链路 :假设A规则绑定了一个链路 关联B,那么受限制的是B
:::
流控效果
快速失败
默认的流控效果,直接失败,抛出异常。可以参考上方所写过的:https://www.yuque.com/chenlaohan/xnax4v/akgnzc#j8UNT
Warm Up
刚开始把 阈值调低,不要让过多的请求访问服务器,导致冲垮服务器,先让服务器一点一点处理,再慢慢加量
预热公式:阈值 除以 coldFactor(默认值为3),经过预热时长后才会达到阈值
默认coldFactor为3,即请求QPS从(threshold / 3)开始,经多少预热时长才逐渐升至设定的QPS阈值。
如下案例,阀值为10,预热时长设置5秒。
系统初始化的阀值为10 / 3约等于3,即阀值刚开始为3,然后经过 5秒后阀值才慢慢升高恢复到10。这样做的目的就是防止瞬间高并发导致系统奔溃
排队等待
排队等待(匀速器):匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细可参考官网:flow-control 例如:/testF
的QPS最大为1,超过的话就排队等待,等待的超时时间为20000ms。
java
@GetMapping("/testH")
public String testH() {
String str = Thread.currentThread().getName()+":testH";
log.info(str);
return str;
}

2022-08-24 15:01:09.324 INFO 10980 --- [nio-8501-exec-7] c.c.s.controller.FlowLimitController : http-nio-8501-exec-7:testH
2022-08-24 15:01:10.324 INFO 10980 --- [nio-8501-exec-6] c.c.s.controller.FlowLimitController : http-nio-8501-exec-6:testH
2022-08-24 15:01:11.325 INFO 10980 --- [nio-8501-exec-8] c.c.s.controller.FlowLimitController : http-nio-8501-exec-8:testH
2022-08-24 15:01:12.325 INFO 10980 --- [nio-8501-exec-5] c.c.s.controller.FlowLimitController : http-nio-8501-exec-5:testH
2022-08-24 15:01:13.325 INFO 10980 --- [nio-8501-exec-9] c.c.s.controller.FlowLimitController : http-nio-8501-exec-9:testH
2022-08-24 15:01:14.324 INFO 10980 --- [io-8501-exec-10] c.c.s.controller.FlowLimitController : http-nio-8501-exec-10:testH
2022-08-24 15:01:15.324 INFO 10980 --- [nio-8501-exec-2] c.c.s.controller.FlowLimitController : http-nio-8501-exec-2:testH
2022-08-24 15:01:16.324 INFO 10980 --- [nio-8501-exec-1] c.c.s.controller.FlowLimitController : http-nio-8501-exec-1:testH
2022-08-24 15:01:17.325 INFO 10980 --- [nio-8501-exec-4] c.c.s.controller.FlowLimitController : http-nio-8501-exec-4:testH
2022-08-24 15:01:18.325 INFO 10980 --- [nio-8501-exec-3] c.c.s.controller.FlowLimitController : http-nio-8501-exec-3:testH
熔断降级规则
除了流量控制以外,对调用链路中不稳定资源的熔断降级,也是保障服务高可用的重要措施之一。 在分布式微服务架构中,一个系统往往由多个服务组成,不同服务之间相互调用,组成复杂的调用链路。如果链路上的某一个服务出现故障,那么故障就会沿着调用链路在系统中蔓延,最终导致整个系统瘫痪。Sentinel 提供了熔断降级机制就可以解决这个问题。 Sentinel 的熔断将机制会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),暂时切断对这个资源的调用,以避免局部不稳定因素导致整个系统的雪崩。熔断降级作为服务保护自身的手段,通常在客户端(调用端)进行配置,资源被熔断降级最直接的表现就是抛出 DegradeException 异常。
官网参考:circuit-breaking
Sentinel 熔断策略
Sentinel 提供了 3 种熔断策略,如下表所示。慢调用比例 (SLOW_REQUEST_RATIO) | 选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大响应时间),若请求的响应时间大于该值则统计为慢调用。 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则再次被熔断。 |
异常比例 (ERROR_RATIO) | 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目且异常的比例大于阈值,则在接下来的熔断时长内请求会自动被熔断。 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。 |
异常数 (ERROR_COUNT) | 当单位统计时长内的异常数目超过阈值之后会自动进行熔断。 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。 |
Sentinel 熔断降级配置规则
熔断降级规则(DegradeRule)包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
资源名 | 即规则的作用对象 | |
熔断策略 | 支持三种策略 慢调用比例/异常比例/异常数 | 慢调用比例 |
最大RT | 请求的最大响应时间,请求的响应时间大于该值则统计为慢调用 | |
熔断时长 | 熔断开启状态持续的时间,超过该时间熔断器会切换为探测恢复状态(HALF-OPEN),单位为 <font style="color:rgb(68, 68, 68);">s</font> |
|
最小请求数 | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
统计时长 | 熔断触发需要统计的时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
比例阈值 | 分为慢调用比例阈值和异常比例阈值,即慢调用或异常调用占所有请求的百分比,取值范围 [0.0,1.0](1.8.0 引入) | |
异常数 | 请求或调用发生的异常的数量(注:策略为异常数策略下可设置) |
Sentinel 熔断状态
Sentinel 1.8.0版本对熔断降级做了大的调整,可自定义任意时长的熔断时间,引入了半开启恢复支持。熔断状态有三种状态,非别为<font style="color:rgb(77, 77, 77);">OPEN、HALF_OPEN、CLOSED</font>
状态 | 说明 | 触发条件 |
---|---|---|
熔断关闭状态(CLOSED) | 处于关闭状态时,请求可以正常调用资源。 | 满足以下任意条件,Sentinel 熔断器进入熔断关闭状态: + 全部请求访问成功。 + 单位统计时长(statIntervalMs)内请求数目小于设置的最小请求数目。 + 未达到熔断标准,例如服务超时比例、异常数、异常比例未达到阈值。 + 处于探测恢复状态时,下一个请求访问成功。 |
熔断开启状态(OPEN) | 处于熔断开启状态时,熔断器会一定的时间(规定的熔断时长)内,暂时切断所有请求对该资源的调用,并调用相应的降级逻辑使请求快速失败避免系统崩溃。 | 满足以下任意条件,Sentinel 熔断器进入熔断开启状态: + 单位统计时长内请求数目大于设置的最小请求数目,且已达到熔断标准,例如请求超时比例、异常数、异常比例达到阈值。 + 处于探测恢复状态时,下一个请求访问失败。 |
探测恢复状态(HALF-OPEN) | 处于探测恢复状态时,Sentinel 熔断器会允许一个请求调用资源。则若接下来的一个请求成功完成(没有错误)则结束熔断,熔断器进入熔断关闭(CLOSED)状态;否则会再次被熔断,熔断器进入熔断开启(OPEN)状态。 | 在熔断开启一段时间(降级窗口时间或熔断时长,单位为 s)后,Sentinel 熔断器自动会进入探测恢复状态。 |
慢调用比例
- 编写业务测试方法
@GetMapping("/testI")
public String testI() {
String str = "serverPort:[" + serverPort + "],testI,测试慢调用比例RT";
try {
//暂停1秒
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info(str);
return str;
}
- 配置给资源熔断规则
此配置代表 在1000ms
统计时长内,总请求数超过5
次请求有80%
的最大RT超过200ms
的就进行熔断2s
- Jmeter压力测试
使用Jmeter模拟 1s
请求10
个线程(大于最小请求数5个的设置)调用/testI
第一步:添加线程组,设置线程数和下方时间。下方表示1s 请求10次![]() |
第二步:添加一个请求![]() ![]() |
---|---|
第三步:发送请求![]() |
应为程序中设置了休眠 1000s
,从实时监控可以看出只是第一个线程请求是OK的,后续9个线程请求都被限流了。
异常比例
1000ms统计时长内,大于5次的请求中超过80%的请求出现异常,则熔断2s
@GetMapping("/testI")
public String testI() {
String str = "serverPort:[" + serverPort + "],testI,测试慢调用比例RT";
try {
//暂停1秒
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info(str);
int i = 10 / 0; //并发测试这里模拟异常
return str;
}
Jmeter 单独访问一次,必然来一次报错一次(int age = 10/0),调一次错一次;开启jmeter后,直接高并发发送请求,多次调用达到我们的配置条件了。断路器开启(保险丝跳闸),微服务不可用了,不再报错error而是服务降级了。
异常数
统计时长1000ms
内,超过5
个请求中有3
个请求异常的熔断2s
浏览器频繁访问 ,会发现次数达到后进行熔断
热点参数限流
何为热点:热点即经常访问的数据,有时候我们希望统计或者限制某个热点数据中访问频次最高的数据,并对其访问进行限流。使用如下:
案例使用
- 定义业务方法
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "blockExceptionTestHotKey")
public String testHotkey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return "serverPort:[" + serverPort + "],testHotkey...";
}
//这里是我们自定义的兜底方法,BlockException不要打成了BlockedException
public String blockExceptionTestHotKey(String p1, String p2, BlockException e) {
return "testHotkey自定义限流提示:blockExceptionTestHotKey╰(*°▽°*)╯...";
}
- 定义热点限制规则
热点规则共有资源名、限流模式(只支持QPS模式)、参数索引、单机阈值、统计窗口时长、是否集群6种参数,还有一些高级选项,用到时会详细介绍。这里会用到注解中的value作为资源名,兜底方法会在后面详细介绍
资源名**:**唯一路径,默认为请求路径。此处必须是 @SentinelResource 注解的 value 属性值,配置@GetMapping 的请求路径无效)
配置热点规则:
绑定testHotKey
资源,把testHotKey
对应的第一个参数作为热点key进行监控。设定热点限流规则:当该资源的访问QPS超过1次/s的时候,产生限流并执行自定义的blockExceptionTestHotKey
兜底方法。
简而言之:方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理。
- 测试
1次/s正常显示,迅速点击两次,触发热点限流,执行自定义兜底方法:
- 仅传入参数p2没有任何影响 http://localhost:8501/testHotKey?h2=b
参数例外项
上述案例演示了第一个参数h1,当QPS超过1秒1次点击后马上被限流
特例情况:我们期望h1参数当它是某个特殊值时,它的限流值和平时不一样,比如当h1的值等于5时,它的阈值可以达到10。
测试
持久化规则
前面我们微服务新增的限流规则后,微服务关闭重启后就会丢失,当时配置的限流规则都是临时的。 如何进行持久化呢,可以将限流配置规则持久化进Nacos保存,只要刷新服务某个rest地址,sentinel控制台的流控规则 就能看到。只要nacos里面的配置不删除,针对8501上的sentinel上的流控规则就持续存在。 (也可以持久化到文件,redis,数据库等)
- 导入持久化需要的依赖,编写业务方法啊
<!--SpringCloud ailibaba sentinel-datasource-nacos 持久化-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public String byUrl() {
return "持久化配置限流测试";
}
- 修改yaml
server:
port: 8501
spring:
application:
name: chen-sentinel-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: 127.0.0.1:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: 127.0.0.1:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8917
web-context-unify: false
#持久化配置
datasource:
ds1:
nacos:
server-addr: 127.0.0.1:8848
dataId: chen-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
- nacos添加配置规则
我们将sentinel的流控配置保存在nacos中,因为我们可以把nacos的配置持久化在了数据库中。
[
{
"resource": "/rateLimit/byUrl",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。
- 测试
启动服务访问接口,刷新Sentinel。可以看到Sentinel中加载了通过nacos持久化的规则配置文件。