Sentinel核心库
Sentinel主页 https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5
Sentinel介绍
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量
控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度
来帮助开发者保障微服务的稳定性。
Sentinel核心组件
1:核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同
时对 Dubbo / Spring Cloud 等框架也有较好的支持。
2:控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。
Sentinel vs Hystrix
对比内容 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支 持 |
支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 不支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器 发现等 |
不完善 |
常见框架的适 配 |
Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet Netflix、Spring Cloud |
Sentinel核心概念
- 资源:资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服
务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
- 规则:围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以
动态实时调整。
Sentinel核心功能
流量控制
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考
虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处
理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据
需要把随机的请求调整成合适的形状,如下图所示:
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
熔断降级
什么是熔断降级
除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系
的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。
Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应
时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资
源而导致级联故障。
Sentinel熔断降级设计
Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的
好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线
程数目过多),还需要预先给各个资源做线程池大小的分配。
Sentinel熔断降级设计:
并发线程数限制:和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源
对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不
稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源
上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
响应时间降级:除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资
源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之
后才重新恢复。
系统自我保护
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高
的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把
本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时
候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证
系统在能力范围之内处理最多的请求。
Sentinel熔断限流
Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合Dashboard 可以取得最好的效果。先来学习Sentinel 核心库的使用,后面再学习Dashboard使用。
在我们项目中,用户请求通过 hailtaxi-gateway 路由到 hailtaxi-driver 或者 hailtaxi-order ,还
有可能在 hailtaxi-order 中使用feign调用 hailtaxi-driver ,所以我们有可能在单个服务中实现熔
断限流,也有可能要集成feign调用实现熔断限流,还有可能在微服务网关中实现熔断限流。我们接下
来一步一步实现每一种熔断限流操作。
入门案例
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.1</version>
</dependency>
public class SentinelDemo {
public static void main(String[] args) {
//配置规则
initFlowRules();
while (true){
try (Entry entry = SphU.entry("test01")){
// 被保护的逻辑
System.out.println("hello world");
} catch (BlockException e) {
// 处理被流控的逻辑
System.out.println("blocked!");
break;
}
}
}
public static void initFlowRules(){
List<FlowRule> flowRules = new ArrayList<FlowRule>();
FlowRule flowRule = new FlowRule();
//设置qps=5
flowRule.setCount(5);
//针对那个资源设置规则
flowRule.setResource("test01");
//限流维度
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRules.add(flowRule);
FlowRuleManager.loadRules(flowRules);
}
}
效果:
启动 Sentinel 控制台
直接下载源码,导入到idea里面然后启动,看看是否有必要修改端口号
Sentinel 开源控制台支持实时监控和规则管理。接入控制台的步骤如下:
(1)下载控制台 jar 包并在本地启动:如果不是下载源码启动的,就见此处文档,可以参见 此处文档。
(2)客户端接入控制台,需要:
客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信。您可以通过 pom.xml 引入 JAR 包:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.1</version>
</dependency>
启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port 指定控制台地址和端口。更多的参数参见 启动参数文档。
-Dcsp.sentinel.dashboard.server=localhost:8089
确保应用端有访问量
完成以上步骤后即可在 Sentinel 控制台上看到对应的应用,机器列表页面可以看到对应的机器:
注意:登录密码:sentinel \sentinel
注解支持
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.1</version>
</dependency>
@SpringBootApplication
public class SentinelDemo {
public static void main(String[] args) {
//配置规则
initFlowRules();
SpringApplication.run(SentinelDemo.class);
}
public static void initFlowRules(){
List<FlowRule> flowRules = new ArrayList<FlowRule>();
FlowRule flowRule = new FlowRule();
//设置qps=5
flowRule.setCount(2);
//针对那个资源设置规则
flowRule.setResource("hello");
//限流维度
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRules.add(flowRule);
FlowRuleManager.loadRules(flowRules);
}
}
@Service
public class TestService {
@SentinelResource(value = "hello",blockHandler = "exceptionHandler")
public String hello(long s){
return String.format("Hello at %d", s);
}
@SentinelResource(value = "hello",fallback = "hellof")
public String hellof(long s){
return String.format("Hello at %d", s);
}
//降级 Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
public String helloFallback(long s) {
return String.format("Halooooo %d", s);
}
// 熔断 Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
}
@RestController
public class TestController {
@Autowired
private TestService testService;
@RequestMapping("hello")
public String hello(){
return testService.hello(23L);
}
@RequestMapping("hellof")
public String hellof(){
return testService.hellof(23L);
}
}
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
Spi扩展点
FlowRuleInitFunc
把上面的流控规则写到这个类里面
public class FlowRuleInitFunc implements InitFunc {
@Override
public void init() throws Exception {
initFlowRules();
}
public static void initFlowRules(){
List<FlowRule> flowRules = new ArrayList<FlowRule>();
FlowRule flowRule = new FlowRule();
//设置qps=5
flowRule.setCount(2);
//针对那个资源设置规则
flowRule.setResource("hello");
//限流维度
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRules.add(flowRule);
FlowRuleManager.loadRules(flowRules);
}
}
在META-INF/services/com.alibaba.csp.sentinel.init.InitFunc文件中,添 加自定义扩展点的全路径
启动测试
SpringBoot集成
Maven依赖
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Sentinel依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
返回值封装
@Data
public class BasicResult<T> {
private String resultCode;
private String errorMsg;
private T data;
}
Controller
@RestController
public class SpringbootsentinelController {
@Autowired
SpringbootsentinelService springbootsentinelService;
@GetMapping("/hello/{name}")
public BasicResult<String> sayHello(@PathVariable("name") String name) throws SystemBlockException {
return springbootsentinelService.doTest(name);
}
}
Service
@Service
public class SpringbootsentinelService {
public BasicResult<String> doTest(String name) throws SystemBlockException {
if ("张三".equals(name)){
throw new SystemBlockException("500","该用户已存在");
}
BasicResult<String> basicResult = new BasicResult<>();
basicResult.setData("hello , "+name);
basicResult.setResultCode("200");
return basicResult;
}
}
启动类
@SpringBootApplication
public class SpringbootsentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootsentinelApplication.class, args);
}
}
如果在SpringBoot项目中使用Sentinel,首先需要引入 spring-cloud-starter-alibaba-sentinel 依
赖,并使用 @SentinelResource 标识资源。
@SentinelResource
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。
@SentinelResource 注解包含以下属性:
value | 资源名称,必需项(不能为空) |
---|---|
blockHandler / blockHandlerClass |
blockHandler 对应处理 BlockException 的函数名称,可选项。♞ blockHandler 函数访问范围需要是 public;♞ 返回类型需要与原 方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的 参数,类型为 BlockException。♞ blockHandler 函数默认需要和 原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必 需为 static 函数,否则无法解析。 |
fallback / fallbackClass | fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:♞ 返回值类型必须与原函数返回值类型一 致;♞ 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。♞ fallback 函数默认 需要和原方法在同一个类中。若希望使用其他类的函数,则可以指 定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需 为 static 函数,否则无法解析。 |
defaultFallback(1.6.0 开 始) |
默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑 (即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类 型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处 理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:♞ 返回值类型必须与原 函数返回值类型一致;♞ 方法参数列表需要为空,或者可以额外多 一个 Throwable 类型的参数用于接收对应的异常。♞ defaultFallback 函数默认需要和原方法在同一个类中。若希望使用 其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对 象,注意对应的函数必需为 static 函数,否则无法解析。 |
exceptionsToIgnore(1.6.0 开始) |
用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 |
entryType | entry 类型,可选项(默认为 EntryType.OUT) |
blockHandler
当name等于张三的时候,此时会报错,代码如下
如果此时访问 http://localhost:8081/hello/张三, 查询司机信息,如果没有name为张三的司机信息,会报如下错误,这种体验非常查,我们可以集成Sentinel使用 @Sentinesource 的blockHandler 返回默认错误信息。
在工程中引入工程中引入 spring-cloud-starter-alibaba-sentinel 依赖,依赖如下
<!--Sentinel依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
我们先添加一个方法 blockExHandler ()用来处理程序发生 BlockException 异常的时候,执行默认操
作,代码如下:
/***
* blockExHandler
* 1)方法入参和被降级方法保持一致,最多多一个BlockException
* 2)返回值要和被降级方法保持一致
*/
public BasicResult blockExHandler(String name, BlockException blockException){
BasicResult basicResult = new BasicResult();
SystemBlockException blockException1 = (SystemBlockException) blockException;
basicResult.setResultCode(blockException1.getResourceName());
basicResult.setErrorMsg(blockException1.getRuleLimitApp());
return basicResult;
}
我们为 doTest() 方法添加一个 @SentinelResource 注解,用来标注资源,表示当前方法需要执行限
流、降级,在注解中添加value属性,用来标注资源,说白了就是给当前资源起个名字,blockHandler
用来表示当前方法发生 BlockException 异常的时候,将处理流程交给指定的方法 blockExHandler()
处理,此时 blockExHandler() 方法必须和抛出异常的方法在同一个类中,这是一种降级操作,代码如
下:
如果此时不在同一个类中,我们可以在 @SentinelResource 中添加 blockHandlerClass 属性,指定
降级处理类的方法所在的类,代码如下:
@SentinelResource(value = "info",blockHandler =
"blockExHandler",blockHandlerClass = "xxx.xxx.Xxxx"
访问http://localhost:8081/hello/张三 测出出错效果如下:
fallback
如果我们希望抛出任何异常都能处理,都能调用默认处理方法,而并非只是 BlockException 异常才调
用,此时可以使用 @SentinelResource 的 fallback 属性,代码如下:
访问http://localhost:8081/hello/张三 测出出错效果如下:
如果发生异常执行的方法和当前发生异常的方法不在同一个类中,可以使用 @SentinelResource 注解
的 fallbackClass 实现,代码如下:
@SentinelResource(value = "info",fallback ="exHandler" ,fallbackClass =
"xx.xxx.xxx.xx.Xxx")
defaultFallback
上面无论是 blockHandler 还是 fallback ,每个方法发生异常,都要为方法独立创建一个处理异常的
方法,效率非常低,我们可以使用 @SentinelResource 注解的 defaultFallback 属性,为一个类指定
一个全局的处理错误的方法,代码如下:
@Service
@SentinelResource(defaultFallback = "defaultFallback")
public class SpringbootsentinelService {
/**
* @param name
* @return 一定要指定
*/
@SentinelResource(value = "info")
public BasicResult<String> doTest(String name) throws RuntimeException {
if ("张三".equals(name)){
throw new RuntimeException("该用户已存在");
}
BasicResult<String> basicResult = new BasicResult<>();
basicResult.setData("hello , "+name);
basicResult.setResultCode("200");
return basicResult;
}
/**
* 异常处理
* @return
*/
public BasicResult<String> defaultFallback(){
BasicResult<String> basicResult = new BasicResult();
basicResult.setResultCode("500");
basicResult.setErrorMsg("系统繁忙,请稍后再试-defaultFallback");
return basicResult;
}
}
此时需要注意, defaultFallback 属性指定的方法入参必须为空,最多可以增加一个异常对象。
我们访问http://localhost:8081/hello/张三 效果如下:
限流降级规则
Sentinel支持多种限流规则,规则我们可以在代码中直接定义,规则属性如下:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式 (0) |
QPS 模式 |
limitApp | 流控针对的调用来源 | default ,代表不区 分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直 接) |
controlBehavior | 流控效果(直接拒绝/WarmUp/匀速+排队等 待),不支持按调用关系限流 |
直接拒绝 |
clusterMode | 是否集群限流 | 否 |
流量控制
理解上面规则的定义之后,我们可以通过调用 FlowRuleManager.loadRules() 方法来用硬编码的方
式定义流量控制规则。
QPS流量控制
先实现流量基于QPS控制,代码如下:
@Configuration
public class SentinelLiuLiang {
/**
* 初始化规则
*/
@PostConstruct
public void initFlowQpsRule(){
//规则集合
List<FlowRule> rulesList = new ArrayList<>();
//定义一个规则 info是资源名称
FlowRule rule = new FlowRule("info");
//设置阀值
rule.setCount(2);
//设置限流类型
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//default,代表不区分调用来源
rule.setLimitApp("default");
//定义规则到集合
rulesList.add(rule);
//加载规则
FlowRuleManager.loadRules(rulesList);
}
}
@Service
@SentinelResource(defaultFallback = "defaultFallback")
public class SpringbootsentinelService {
/**
* @param name
* @return 一定要指定
*/
@SentinelResource("info")
public BasicResult<String> doTest(String name) throws RuntimeException {
BasicResult<String> basicResult = new BasicResult<>();
basicResult.setData("hello , "+name);
basicResult.setResultCode("200");
return basicResult;
}
/**
* 异常处理
* @return
*/
public BasicResult<String> defaultFallback(){
BasicResult<String> basicResult = new BasicResult();
basicResult.setResultCode("500");
basicResult.setErrorMsg("系统繁忙,请稍后再试-defaultFallback");
return basicResult;
}
}
访问http://localhost:8081/hello/张三 此时不会抛出异常,但是频繁刷新,则会调用降
级方法,这个是用Jmter压测的效果如下:
QPs流量控制详解
当 QPS 超过某个阈值的时候,则采取措施进行流量控制行为(类似于我们前面说过的限流算
法上的差异),Sentinel提供了四种流量控制行为
- 直接拒绝(CONTROL_BEHAVIOR_DEFAULT)
- Warm Up(CONTROL_BEHAVIOR_WARM_UP)
- 匀速排队(CONTROL_BEHAVIOR_RATE_LIMITER,漏桶算法 )
- 冷启动+匀速器(CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER),除了让流量缓慢增加,还控制的了请求的间隔时间,让请求已均匀速度通过。这种策略是1.4.0版本新增 的。
这四个行为,是通过FlowRule中的controlBehavior属性来控制,默认是直接拒绝。
直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。代码案例
public static void main(String[] args) {
initFlowRule();
while (true){
try (Entry entry = SphU.entry("CONTROL_BEHAVIOR_DEFAULT")){
System.out.println("CONTROL_BEHAVIOR_DEFAULT");
}catch (Exception e) {
// 处理被流控的逻辑
System.out.println("blocked!");
break;
}
}
}
private static void initFlowRule(){
List<FlowRule> flowRules = new ArrayList<>();
FlowRule flowRule = new FlowRule();
flowRule.setCount(2);//控制流量
flowRule.setResource("CONTROL_BEHAVIOR_DEFAULT");//资源
flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);//限流行为
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//限流类型
flowRules.add(flowRule);
FlowRuleManager.loadRules(flowRules);
}
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过”冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
public static void main(String[] args) {
initFlowRule();
while (true){
try (Entry entry = SphU.entry("CONTROL_BEHAVIOR_WARM_UP")){
System.out.println("CONTROL_BEHAVIOR_WARM_UP");
}catch (Exception e) {
// 处理被流控的逻辑
System.out.println("blocked!");
break;
}
}
}
private static void initFlowRule(){
List<FlowRule> flowRules = new ArrayList<>();
FlowRule flowRule = new FlowRule();
flowRule.setCount(20);//控制流量
flowRule.setResource("CONTROL_BEHAVIOR_WARM_UP");//资源
flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);//限流行为
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//限流类型
flowRule.setWarmUpPeriodSec(10);//代表期待系统进入稳定状态的时间(即预热时长).
flowRules.add(flowRule);
FlowRuleManager.loadRules(flowRules);
}
}
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
当请求数量远远大于阈值时,这些请求会排队等待,这个等待时间可以设置,如果超过等待时
间,那这个请求会被拒绝。
如下图所示,假设qps=5,表示请求每200ms才能通过1个,多处的请求排队等待,超时时间达
标最大排队时间,超过最大排队时间则直接拒绝。
基于调用关系的流量控制
线程数流量控制
我们修改限流阈值类型,代码如下:
此时再来访问http://localhost:8081/hello/张三 发现用浏览器无论怎么访问都不会出现
降级现象,但是如果用Jmeter模拟多个线程,效果就不一样了,效果如下:
效果不是很好验证,需要多循环几次
熔断降级
熔断降级规则包含下面几个重要的属性:
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() 方法来用硬编码的方式定义流量控制规则,在SentinelLiuLiang规则定义如下:
/**
* 熔断降级规则
*/
@PostConstruct
public void initDegradeRule(){
//降级规则集合
List<DegradeRule> ruleList = new ArrayList<>();
DegradeRule degradeRule = new DegradeRule("info");
//设置触发降级阀值
degradeRule.setCount(2);
degradeRule.setMinRequestAmount(1);
//熔断降级策略,支持慢调用比例/异常比例/异常数策略
//DEGRADE_GRADE_RT:平均响应时间
//DEGRADE_GRADE_EXCEPTION_RATIO:异常比例数量
//DEGRADE_GRADE_EXCEPTION_COUNT:异常数
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
//熔断窗口时长 单位s
degradeRule.setTimeWindow(30);
//设置统计时间
degradeRule.setStatIntervalMs(1000);
//将规则添加到集合
ruleList.add(degradeRule);
//加载规则
DegradeRuleManager.loadRules(ruleList);
}
测试一下平均响应时间,在程序中休眠10秒中,再执行访问,代码如下:
测试效果如下:
我们可以发现只有2个访问是成功的,并且熔断降级10秒钟之后才可接着访问该方法。
系统自我保护
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体
平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量
和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则包含下面几个重要的属性
Field | 说明 | 默认值 |
---|---|---|
highestSystemLoad | load1 触发值,用于触发自适应控制阶段 | -1 (不生效) |
avgRt | 所有入口流量的平均响应时间 | -1 (不生效) |
maxThread | 入口流量的最大并发数 | -1 (不生效) |
qps | 所有入口资源的 QPS | -1 (不生效) |
highestCpuUsage | 当前系统的 CPU 使用率(0.0-1.0) | -1 (不生效) |
理解上面规则的定义之后,我们可以通过调用 SystemRuleManager.loadRules() 方法来用硬编码的
方式定义流量控制规则。
在 配置类中创建如下方法,代码如下:
/**
* 系统自我保护
*/
@PostConstruct
private void initSystemRule() {
//系统自我保护集合
List<SystemRule> rules = new ArrayList<>();
//创建系统自我保护规则
SystemRule rule = new SystemRule();
//CPU使用率 值为0-1,-1 (不生效)
rule.setHighestCpuUsage(0.2);
//所有入口资源的 QPS,-1 (不生效)
rule.setQps(10);
//入口流量的最大并发数,-1 (不生效)
rule.setMaxThread(5);
//所有入口流量的平均响应时间,单位:秒,-1 (不生效)
rule.setAvgRt(5);
//load1 触发值,用于触发自适应控制阶段,系统最高负载,建议取值 CPU cores * 2.5
rule.setHighestSystemLoad(20);
//将规则加入到集合
rules.add(rule);
SystemRuleManager.loadRules(rules);
}
我们可以测试CPU使用率自我保护,如下效果:
https://www.jianshu.com/p/3668cc1bf24a
热点数据
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数
据,并对其访问进行限制。比如:
1:商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
2:用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调
用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效 。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点
参数限流支持集群模式。
要使用热点参数限流功能,需要引入以下依赖:
<!--热点参数-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.8.1</version>
</dependency>
然后为对应的资源配置热点参数限流规则,并在 entry 的时候传入相应的参数,即可使热点参数限流
生效。热点参数规则( ParamFlowRule )类似于流量控制规则( FlowRule ):
属性 | 说明 | 默认值 |
---|---|---|
resource | 资源名,必填 | |
count | 限流阈值,必填 | |
grade | 限流模式 | QPS 模 式 |
durationInSec | 统计窗口时间长度(单位为秒),1.6.0 版本开始支持 | 1s |
controlBehavior | 流控效果(支持快速失败和匀速排队模式),1.6.0 版本开 始支持 |
快速失 败 |
maxQueueingTimeMs | 最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本 开始支持 |
0ms |
paramIdx | 热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置 |
|
paramFlowItemList | 参数例外项,可以针对指定的参数值单独设置限流阈值, 不受前面 count 阈值的限制。仅支持基本类型和字符串 类型 |
clusterMode | 说明 是否是集群参数流控规则 | 默认值 false |
---|---|---|
clusterConfig | 集群流控相关配置 |
我们可以创建一个司机筛选方法,比如根据城市来筛选,在 DriverController 中创建一个方法:
我们访问 http://localhost:8081/driver/search/shenzhen 的时候,连续执行5次,才会限流,
效果如下:
我们访问 http://localhost:18081/driver/search/tj 的时候,连续执行2次,就会限流,效果如
下:
OpenFeign支持
Sentinel 适配了 Feign 组件。如果想使用,除了引入 spring-cloud-starter-alibaba-sentinel 的依
赖外还需要 2 个步骤:
1:配置文件打开 Sentinel 对 Feign 的支持:feign.sentinel.enabled=true
2:加入 spring-cloud-starter-openfeign 依赖使 Sentinel starter 中的自动化配置类生效
在上面案例中,我们可以实现用户打车成功调用 hailtaxi-order 执行下单,并且通过feign调用
hailtaxi-driver 修改司机状态,此时我们可以使用Sentinel实现Feign调用降级、限流。把之前的案例中 @SentinelResource 相关注解全部注释掉,再实现Feign集成。
在 hailtaxi-driver 中引入 OpenFeign 依赖,配置如下:
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!--Openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
还需要在Feign调用客户端(也就是 hailtaxi-order )中开启Feign的支持,配置如下:
feign:
#开启Sentinel对Feign的支持
sentinel:
enabled: true
为了测试程序异常能实现降级操作,我们在 hailtaxi-order 中将 OrderInfoController.add() 方法
的司机ID改成一个不存在的司机ID,让程序报错,测试降级处理,代码如下:
fallback
可以为Feign接口创建一个实现类,在实现类中处理程序异常降级处理方法,代码如下:
public class DriverFeignFallback implements DriverFeign{
/**
* status()降级处理方法
*/
@Override
public Driver status(String id, Integer status) {
Driver driver = new Driver();
driver.setId(id);
driver.setStatus(status);
driver.setName("系统比较繁忙,请您稍后再试!");
return driver;
}
}
我们还需要在Feign接口上添加 fallback 属性指定讲解处理的类,代码如下:
FeignClient(value = "hailtaxi-driver",fallback = DriverFeignFallback.class)
fallbackFactory
可以为Feign接口创建一个降级处理的工厂对象,在工厂对象中处理程序异常降级处理方法,代码
如下:
@Component
public class DriverFeignFallback implements FallbackFactory<DriverFeign>{
@Override
public DriverFeign create(Throwable throwable) {
return new DriverFeign() {
/**
* status()降级处理方法
*/
@Override
public Driver status(String id, Integer status) {
Driver driver = new Driver();
driver.setId(id);
driver.setStatus(status);
driver.setName("系统比较繁忙,请您稍后再试!");
return driver;
}
};
}
}
我们还需要在Feign接口上添加 fallbackFactory 属性指定讲解处理的类,代码如下:
@FeignClient(value = "hailtaxi-driver",fallbackFactory =
DriverFeignFallbackFactory.class)
测试效果如下
Sentinel集成Gateway
Sentinel对网关支持
Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。
Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规
则和自定义 API 的实体和管理逻辑:
- GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
- ApiDefinition : 用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api ,请求 path 模式为 /foo/ 和 /baz/ 的都归到 my_api 这个 API分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
其中网关限流规则 GatewayFlowRule 的字段解释如下:
- resource :资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
- resourceMode :规则是针对 API Gateway 的 route( RESOURCE_MODE_ROUTE_ID )还是用户在
Sentinel 中定义的 API 分组( RESOURCE_MODE_CUSTOM_API_NAME ),默认是 route。
- grade :限流指标维度,同限流规则的 grade 字段。
- count :限流阈值
- intervalSec :统计时间窗口,单位是秒,默认是 1 秒。
- controlBehavior :流量整形的控制效果,同限流规则的 controlBehavior 字段,目前支持快
速失败和匀速排队两种模式,默认是快速失败。 - burst :应对突发请求时额外允许的请求数目。
- maxQueueingTimeoutMs :匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下
生效。 - paramItem :参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规
则;否则会转换成热点规则。其中的字段:- parseStrategy :从请求中提取参数的策略,目前支持提取来源IP(PARAM_PARSE_STRATEGY_CLIENT_IP )、Host( PARAM_PARSE_STRATEGY_HOST )、任意 Header( PARAM_PARSE_STRATEGY_HEADER )和任意 URL 参数 ( PARAM_PARSE_STRATEGY_URL_PARAM )四种模式。
- fieldName :若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。
- pattern :参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则
统计该请求属性的所有值。(1.6.2 版本开始支持)
- matchStrategy :参数值的匹配策略,目前支持精确匹配( PARAM_MATCH_STRATEGY_EXACT )、子串匹配( PARAM_MATCH_STRATEGY_CONTAINS )和正则匹配( PARAM_MATCH_STRATEGY_REGEX )。(1.6.2 版本开始支持)
- 用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则,或通过GatewayRuleManager.register2Property(property) 注册动态规则源动态推送(推荐方式)。
创建Gateway网关项目
maven依赖
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Sentinel-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
</dependencyManagement>
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class SpringbootsentinelgatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootsentinelgatewayApplication.class, args);
}
}
bootstrap.properties
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
# Nacos认证信息
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos
spring.cloud.nacos.config.contextPath=/nacos
# 设置配置中心服务端地址
spring.cloud.nacos.config.server-addr=localhost:8848
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.gateway.routes[0].id=springbootsentinel
spring.cloud.gateway.routes[0].uri=lb://springbootsentinel
spring.cloud.gateway.routes[0].predicates[0]=Path=/hello/**,/search/**
server.port=8083
application.properties
# 应用名称
spring.application.name=springbootsentinelgateway
集成Sentinel
如果想要让微服务网关集成Sentinel,需要引入依赖包,使用时只需注入对应的SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。首先在 gateway 中引入如下依赖:
<!--Sentinel-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.8.1</version>
</dependency>
实例引入:创建配置类 com.itheima.config.GatewayConfiguration :
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>>
viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/***限流的异常处理器
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler
sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer);
}
/**
* Sentinel路由处理核心过滤器
* @return
*/
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
}
配置/API定义
API定义
ApiDefinition 用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如可以定义一个 API 叫 my_api ,请求 path 模式为 /foo/ 和 /baz/ 的都归到 my_api 这个API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
在配置类 com.itheima.config.GatewayConfiguration 创建Api:
/**
* Api定义
*/
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition cartApi = new ApiDefinition()
.setPredicateItems(new HashSet<>(){{
add(new ApiPathPredicateItem().setPattern("/hello/**"));
add(new ApiPathPredicateItem().setPattern("/car/model/**"));
add(new ApiPathPredicateItem().setPattern("/trip/message")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
//参数值的匹配策略
// 精确匹配(PARAM_MATCH_STRATEGY_EXACT)
// 子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)
// 正则匹配(PARAM_MATCH_STRATEGY_REGEX)
}});
definitions.add(cartApi);
//加载API
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
规则创建
GatewayFlowRule 网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route
或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
在配置类 com.itheima.config.GatewayConfiguration 创建配置:
/**
* 规则定义
*/
private void initGatewayRules(){
//网关限流规则
Set<GatewayFlowRule> rules = new HashSet<GatewayFlowRule>();
//资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
rules.add(new GatewayFlowRule("springbootsentinel")
//限流阈值
.setCount(2)
//应对突发请求时额外允许的请求数目。
.setBurst(2)
//统计时间窗口,单位是秒,默认是 1 秒。
.setIntervalSec(1)
//限流行为
//CONTROL_BEHAVIOR_RATE_LIMITER 匀速排队
//CONTROL_BEHAVIOR_DEFAULT 快速失败(默认)
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
//匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
.setMaxQueueingTimeoutMs(1000)
);
//API 【mall_cart_api】限流配置
rules.add(new GatewayFlowRule("hailtaxi_driver_api")
//限流阈值
.setCount(3)
//统计时间窗口,单位是秒,默认是 1 秒。
.setIntervalSec(1)
);
//加载网关规则
GatewayRuleManager.loadRules(rules);
}
配置/API加载
上面两个方法创建了API并且创建了配置,但并没有在程序启动加载,可以采用 @PostConstruct
注解实现加载调用,在 com.itheima.config.GatewayConfiguration 中创建方法:
/***
*初始化加载Api和规则
*/
@PostConstruct
public void doInit() {
initCustomizedApis();
initGatewayRules();
}
Sentinel控制台
Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点
链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效
果。
Sentinel控制台的安装
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规
则管理和推送的功能。
Sentinel 控制台包含如下功能:
- 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
- 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,
最终可以实现秒级的实时监控。 - 规则管理和推送:统一管理推送规则。
- 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。
Sentinel控制台安装可以基于jar包启动的方式安装,也可以基于docker安装,为了方便操作,我们这里
采用docker安装方式:
docker run --name=sentinel-dashboard -d -p 8858:8858 -d --restart=always
bladex/sentinel-dashboard
安装好了后,可以直接访问 http://192.168.211.145:8858 访问控制台,默认用户名和密码都是
sentinel :
登录后,效果如下:
本地安装,下载jar包,sentinel-dashboard-1.8.0.jar,下载1.8.0
启动
java -jar -Dserver.port=8858 sentinel-dashboard-1.8.1.jar
登录效果如下:
客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信,可以通过 pom.xml 引入 JAR 包:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>x.y.z</version>
</dependency>
如果是SpringBoot工程接入Sentinel,可以直接引入如下依赖包:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
接入控制台
将Gateway微服务网关接入Sentinel控制台,首先引入依赖包:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
在核心配置文件中配置Sentinel服务地址
# Sentinel 控制台地址
spring.cloud.sentinel.transport.dashboard=localhost:8858
#sentinel会开启一个端口号 默认为8719
spring.cloud.sentinel.transport.port=8719
这里的 spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 HttpServer,该 Server 会与 Sentinel 控制台做交互,比如限流规则拉取。此时出发一些请求操作,再看Sentinel控制台会多一个服务控:
可视化管理
实时监控
同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在”实时监控”下。注意: 实时监控仅存储 5 分钟以内的数据,如果需要持久化,需要通过调用实时监控接口来定制。
如果要获取监控数据,直接调用 http://localhost:8719/clusterNode 即可获取,效果如下:
流控规则
可以在【流控规则】页面中新增,点击【流控规则】进入页面新增页面,如下图:
资源名:其实可以和请求路径保持一致,这里的流控模式为QPS,触发流控执行阈值为1,流控模式为让当前请求的资源快速直白。
测试效果如下:
这里的参数和我们程序中的参数其实是一样的,如下说明
resource:资源名,即限流规则的作用对象
count: 限流阈值
grade: 限流阈值类型(QPS 或并发线程数)
limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
strategy: 调用关系限流策略
controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
流控效果和程序中也是一样的:
快速失败:
当QPS超过任何规则的阈值后,新的请求就会立即拒绝,拒绝方式为抛出FlowException . 这种方
式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
Warm Up:
当系统长期处理低水平的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压
垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值的上限,给系统一个预热的时
间,避免冷系统被压垮。
排队等待:
匀速排队严格控制请求通过的时间间隔,也即是让请求以均匀的速度通过,对应的是漏桶算法。
降级规则
可以选择 降级规则>新增降级规则 ,如下图:
降级规则的熔断策略有3种,分别是慢调用比例、异常比例、异常数,和程序中是一样的。
热点数据
热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访
问进行限制。