目录
- 5. Alibaba微服务组件Sentinel(SpringCloud中的熔断服务Hystrix)
- 5.1 熔断服务
- 5.2 Sentinel:分布式系统的流量防卫兵
- 5.3 Sentinel 快速开始
- 5.4 Sentinel控制台部署
- 5.5 Sentinel---整合SpringCloud Alibaba
- 5.6 Sentinel---并发线程数---流控规则
- 5.7 Sentinel---BlockException统一异常处理
- 5.8 Sentinel---关联流控模式
- 5.9 Sentinel---链路流控模式
- 5.10 流控效果
- 5.11 Sentinel---排队等待
- 5.12 Sentinel---熔断降级规则
- 5.13 Sentinel---整合Openfeign降级
- 5.14 Sentinel---热点参数流控
- 5.15 Sentinel---系统保护规则
- 5.16 Sentinel---规则持久化
上一篇文章《微服务组件Feign&Nacos配置中心使用》
5. Alibaba微服务组件Sentinel(SpringCloud中的熔断服务Hystrix)
5.1 熔断服务
- 服务雪崩
概念
服务雪崩效应:因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程
导致服务不可用的原因:
-
激增流量
- 激增流量导致系统 CPU / Load 飙高,无法正常处理请求
- 激增流量打垮冷系统(数据库连接未创建,缓存未预热)
- 消息投递速度过快,导致消息处理积压
-
不稳定服务依赖
- 慢 SQL 查询卡爆连接池
- 第三方服务不响应,卡满线程池
- 业务调用持续出现异常,产生大量的副作用
-
解决方案
稳定性、恢复性
常见的容错机制
- 超时机制
在不做任何处理的情况下,服务提供者不可用会导致消费者请求线程强制等待,而造成系统资源消耗。加入超时机制,就释放资源。由于释放资源速度较快,一定程度上可以抑制资源耗尽的问题
- 服务限流
规定服务一定的访问量所能承受的临界值,如下图,提前每秒查询量规定为500,当有800访问量时,300会进行限流,可以对着300访问进行降级(设置一个请求,返回请求过多,请稍后再试),从而有效地保护服务器
- 隔离
原理:用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,则会进行降级处理,用户的请求不会被阻塞,至少可以看到一个执行结果(例如返回友好地提醒信息),而不是无休止的等待或者看到系统崩溃
- 服务熔断
远程服务不稳定或网络抖动时暂时关闭,就叫服务熔断
就好比现实世界的断路器,断路器实时监控电路的情况,如果发现电路电流异常,就会跳闸,从而防止电路被烧毁
软件世界的断路器可以这样理解:实时监控应用,如果发现在一定时间内失败次数/失败率达到一定阈值,就"跳闸",断路器打开—此时,请求直接返回,而不去调用原本调用的接口。跳闸一段时间后(例如1分钟),断路器会进行半开状态,这是一个瞬间态,此时允许一次请求调用该调的接口,如果成功,则断路器关闭,应用正常调用;如果调用依旧不成功,断路器继续回到打开状态,过段时间再进入半开状态尝试—通过"跳闸",应用可以保护自己,而且避免浪费资源;而通过半开的设计,可实现应用的"自我修复"
同理,当依赖的服务有大量超时时,再让新的请求去访问根本没有意义,只会无谓的消耗现有的资源。比如我们设置了超时时间为1s,如果短时间内有大量请求实在1s内都得不到响应,就意味着这个服务出现了异常,此时就没有必要再让其他的请求去访问这个依赖了,这个时候就应该使用断路器避免资源浪费
出现的情况有如下
-
出现了慢SQL,慢SQL导致应用越来越慢,最后整个应用卡挂了
-
应用依赖了第三方的服务,第三方服务突然不响应了,造成应用线程也被挂在第三方应用上无法返回,最后自己的线程也耗尽,也无法处理新的请求
-
程序内部某个方法调用持续异常。这个时候调用这个方法已经毫无意义了,而且会影响主业务流程
-
服务降级
所谓服务降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个缺省值。例如:(备用接口/缓存/mock数据)。这样做,虽然服务水平下降,但是可用,比直接服务器挂掉要强。通常在较弱的依赖配置降级服务
5.2 Sentinel:分布式系统的流量防卫兵
Sentinel是什么
Sentinel 是阿里巴巴开源的,面向分布式服务架构的高可用防护组件
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
多维度的流控降级能力
Sentinel 具有以下特征:
- 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等
- 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况
- 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel
- 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等
阿里云提供了企业级的Sentinel服务,应用高可用服务 AHAS
- Sentinel 的主要特性
- Sentinel 的工作原理
Sentinel 和 Hystrix 对比
Sentinel | Hystrix | |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架适配 | Servlet、Spring Cloud、Dubbo、gRPC等 | Servlet、Spring Cloud Netflix |
5.3 Sentinel 快速开始
-
如何使用英文文档:
https://github.com/alibaba/Sentinel/wiki/How-to-Use -
如何使用中文文档:https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8
5.3.1 规则的种类
Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略
Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则
简介
Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果
使用 Sentinel 来进行资源保护,主要分为几个步骤:
-
定义资源
-
定义规则
-
检验规则是否生效
5.3.2 Sentinel—流控规则
(一般设置在服务提供方
)
测试
- 新建一个 springboot 项目springboot-provider-dept-sentinel-8005
- 导入依赖
pom.xml
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--sentinel 核心库-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>
<!--使用@SentinelResource-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 设置端口
server:
port: 8005
- 编写一个控制类
package com.vinjcent.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@RestController
@Slf4j //用作当前类的日志输出的
public class UserController {
private static final String RESOURCE_NAME = "hello";
private static final String USER_RESOURCE_NAME = "user";
private static final String DEGRADE_RESOURCE_NAME = "degrade";
//进行sentinel流控
@RequestMapping(value = "/hello")
public String hello(){
Entry entry = null;
try {
//sentinel 针对资源名称进行限制的请求的接口
entry = SphU.entry(RESOURCE_NAME);
//被保护的业务逻辑
String str = "hello world";
log.info("====" + str + "====");
return str;
} catch (BlockException e) {
//资源访问阻止,被限流或被降级
//进行相应的处理操作
log.info("block");
return "被流控了!";
} catch (Exception e){
//若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(e,entry);
}finally {
if(entry != null){
entry.exit();
}
}
return null;
}
/*
spring 的初始化方法
*/
@PostConstruct //在spring容器创建bean的时候,就会对其进行初始化 <bean init-method="" />
private static void initFlowRules(){
//流控规则集合,因为可能不止监控一个接口
List<FlowRule> rules = new ArrayList<FlowRule>();
//流控
FlowRule rule = new FlowRule();
//设置受保护的资源,为哪个资源进行流量控制
rule.setResource(RESOURCE_NAME);
//设置流控规则 QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//设置受保护的资源阈值
//1s之内不能超过一次访问,不然就会进行流控
rule.setCount(1);
rules.add(rule);
//加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
- 启动启动类
- 测试访问
每隔一秒访问一次http://localhost:8005/hello
快速访问http://localhost:8005/hello
- 控制台
流量控制规则 (FlowRule)
流量规则的定义
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 |
limitApp | 流控针对的调用来源 | default ,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流 | 直接拒绝 |
clusterMode | 是否集群限流 | 否 |
注解@SentinelResource
-
降低接口的侵入性,降低耦合
-
使用注解改善接口中资源定义和被流控降级后的处理方法
【注意】注解方式埋点不支持 private 方法
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource
注解包含以下属性:
-
value
:资源名称,必需项(不能为空) -
entryType
:entry 类型,可选项(默认为EntryType.OUT
) -
blockHandler
/blockHandlerClass
:blockHandler
对应处理BlockException
的函数名称,可选项。blockHandler 函数访问范围需要是public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException
。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定blockHandlerClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析
fallback/fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore
里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
-
返回值类型必须与原函数返回值类型一致
-
方法参数列表需要和原函数一致,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常 -
fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析 -
defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了
exceptionsToIgnore
里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:- 返回值类型必须与原函数返回值类型一致
- 方法参数列表需要为空,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常 - defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析
-
exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出
1.8.0 版本开始,defaultFallback
支持在类级别进行配置。
【注】1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException
)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException
时只会进入 blockHandler
处理逻辑。若未配置 blockHandler
、fallback
和 defaultFallback
,则被限流降级时会将 BlockException
直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException
)
- 测试
- 在controller接口中增加一个接口getUser,并修改initFlowRules方法
package com.vinjcent.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.vinjcent.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@RestController
@Slf4j //用作当前类的日志输出的
public class UserController {
private static final String RESOURCE_NAME = "hello";
private static final String USER_RESOURCE_NAME = "user";
private static final String DEGRADE_RESOURCE_NAME = "DEGRADE";
//进行sentinel流控
@RequestMapping(value = "/hello")
public String hello(){
Entry entry = null;
try {
//sentinel 针对资源名称进行限制的请求的接口
entry = SphU.entry(RESOURCE_NAME);
//被保护的业务逻辑
String str = "hello world";
log.info("====" + str + "====");
return str;
} catch (BlockException e) {
//资源访问阻止,被限流或被降级
//进行相应的处理操作
log.info("block");
return "被流控了!";
} catch (Exception e){
//若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(e,entry);
}finally {
if(entry != null){
entry.exit();
}
}
return null;
}
/*
spring 的初始化方法
*/
@PostConstruct //在spring容器创建bean的时候,就会对其进行初始化 <bean init-method="" />
private static void initFlowRules(){
//流控规则集合,因为可能不止监控一个接口
List<FlowRule> rules = new ArrayList<FlowRule>();
//添加规则一
//流控
FlowRule rule1 = new FlowRule();
//设置受保护的资源,为哪个资源进行流量控制
rule1.setResource(RESOURCE_NAME);
//设置流控规则 QPS
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
//设置受保护的资源阈值
//1s之内不能超过一次访问,不然就会进行流控
rule1.setCount(1);
rules.add(rule1);
//添加规则二
FlowRule rule2 = new FlowRule();
rule2.setResource(USER_RESOURCE_NAME);
rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule2.setCount(1);
rules.add(rule2);
//加载配置好的规则
FlowRuleManager.loadRules(rules);
}
/**
* @SentinelResource 改善接口中资源定义和被流控降级后的处理方法
* 使用需要
* 1. 添加依赖 <artifactId>sentinel-annotation-aspectj</artifactId>
* 2. 在config中配置注解支持的bean SentinelResourceAspect
* value 定义资源
* blockHandler 设置流控降级后的处理方法(默认该方法必须声明在同一个类中)
* 如果不想在同一个类里面,可以把它放在pojo对象当中,同时设置该方法为静态方法
* fallback 当接口出现了异常,就可以交给fallback指定的方法进行处理
* 如何 blockHandler 和 fallback 都同时设置了,则 blockHandler 优先级更高
* exceptionsToIgnore 排除哪些异常可以不同处理,如ArithmeticException.class
* @param id
* @return
*/
@RequestMapping("/user")
@SentinelResource(value = USER_RESOURCE_NAME,
blockHandler = "blockHandlerForFlow",
fallback = "fallbackHandlerForFlow",
exceptionsToIgnore = {ArithmeticException.class})
public User getUser(String id){
int a = 1/0;
return new User("vinjcent");
}
/**
* 注意点:
* 1. 一定要是public
* 2. 返回值和原方法必须保证一致
* 3. 参数也要包含原方法的参数,并且顺序要一致
* 4. 可以在参数最后添加 BlockException 异常,可以区分是什么规则的处理方法
* @param id
* @param e
* @return
*/
public User blockHandlerForFlow(String id, BlockException e){
e.printStackTrace();
return new User("流控!");
}
public User fallbackHandlerForFlow(String id, Throwable e){
e.printStackTrace();
return new User("异常!");
}
}
- 创建一个配置类 SentinelConfig.class
package com.vinjcent.config;
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SentinelConfig {
@Bean
public SentinelResourceAspect sentinelResourceAspect(){
return new SentinelResourceAspect();
}
}
- 重新启动启动类
- 情景一:在没有设置 fallback 之前,只有blockHandler
- 情景二:在设置了fallback之后(这里故意设置一个 ArithmeticException.class异常)
- 情景三:
快速访问http://localhost:8005/user
同时,我们可以设置exceptionsToIgnore
属性排除一些不必要的异常
5.3.3 Sentinel—降级规则
(一般设置在服务消费方
)
概述
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置
针对Sentinel 1.8.0 及以上版本
熔断策略
Sentinel 提供以下几种熔断策略:
- 慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断 - 异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100% - 异常数 (
ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断
熔断降级规则 (DegradeRule)
熔断降级规则包含下面几个重要的属性
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 引入) |
- 在原有的UserController.class添加以下代码
/*
降级规则
*/
@PostConstruct
private void initDegradeRules(){
/* 降级规则 异常 */
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource(DEGRADE_RESOURCE_NAME);
//设置规则的策略: 异常数
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
//触发熔断阈值(异常数): 2
rule.setCount(2);
//出发熔断最小的请求数
rule.setMinRequestAmount(2);
//多长时间段内,触发的阈值熔断 单位: ms
rule.setStatIntervalMs(60*1000);
//触发条件: 一分钟内,执行了两次请求,并出现了2次异常,就会触发熔断
//(熔断降级独有的)熔断的持续时长 单位: s
//一旦出发了熔断,再次请求对应的接口就会直接调用 降级方法
//10s过了之后,就会进入半开状态,并恢复接口请求调用
//如果恢复之后再一次请求就出现了异常,且再次熔断,就不会根据设置的条件进行判断
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
@RequestMapping(value = "/degrade")
@SentinelResource(
value = DEGRADE_RESOURCE_NAME,
entryType = EntryType.IN,
blockHandler = "blockHandlerForDegrade"
)
public User degrade(String id){
//异常数\比例
throw new RuntimeException("异常");
// return new User("正常!");
}
public User blockHandlerForDegrade(String id,BlockException e){
return new User("熔断降级!");
}
- 重启服务
- 测试访问
【注意】在使用@SentinelResource
注解时,参数中的blockHandler
要遵循以下规则
-
blockHandler 函数访问范围需要是
public
-
参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为
BlockException
-
返回值类型必须与原函数返回值类型一致
-
方法参数列表需要和原函数一致,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常 -
fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析
5.4 Sentinel控制台部署
- 根据SpringCloud Alibaba版本下载对应的Sentinel.jar包
下载好后,找到下载位置,通过命令窗口运行
java -jar sentinel-dashboard-1.8.0.jar
- 访问http://127.0.0.1:8080/#/login,如果无响应,在命令窗口按下回车
默认密码用户名均为:sentinel
- 修改端口号、用户名和密码
java -Dserver.port=8858 -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456 -jar sentinel-dashboard-1.8.0.jar
- 如果不想每次都启动时输入参数,可以写一个
sentinel_dashbord.bat
文件,内容如下
java -Dserver.port=8858 -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456 -jar "D:\Program Files\Sentinel\sentinel-dashboard-1.8.0.jar"
pause
每次启动双击该文件即可
5.4.1 客户端接入控制台
引入JAR包
<!--整合sentinel控制台-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.0</version>
</dependency>
配置启动参数
启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port
指定控制台地址和端口。若启动多个应用,则需要通过 -Dcsp.sentinel.api.port=xxxx
指定客户端监控 API 的端口(默认是 8719)
重新启动该服务
访问任意一接口
查看监控控制台
5.4.2 控制台配置项
控制台的一些特性可以通过配置项来进行配置,配置项主要有两个来源:System.getProperty()
和 System.getenv()
,同时存在时后者可以覆盖前者
通过环境变量进行配置时,因为不支持
.
所以需要将其更换为_
配置项 | 类型 | 默认值 | 最小值 | 描述 |
---|---|---|---|---|
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 | 距离最近心跳时间超过指定时间是否自动删除失联节点,默认关闭 |
sentinel.dashboard.unhealthyMachineMillis | Integer | 60000 | 30000 | 主机失联判定,不可关闭 |
server.servlet.session.cookie.name | String | sentinel_dashboard_cookie | - | 控制台应用的 cookie 名称,可单独设置避免同一域名下 cookie 名冲突 |
5.5 Sentinel—整合SpringCloud Alibaba
创建一个新的模块 springcloud-provider-dept-sentinel-8005
- 引入依赖
<dependencies>
<!--web-->
<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>
</dependencies>
- 添加yml配置,为微服务设置sentinel控制台地址
server:
port: 8005
spring:
application:
name: springcloud-provider-dept-sentinel # 应用名称
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8858 # 监控地址
- 添加Sentinel后,需要暴露actuator/sentinel端点,而SpringBoot默认是没有暴露该端点,所以需要设置,测试
- 编写一个controller
@RestController
@RequestMapping("/dept")
@SuppressWarnings("all")
public class DeptController {
@Value("${server.port}")
private int port;
@GetMapping("/msg")
public String getMessage(){
return "获取服务成功!"+port;
}
}
- 启动服务,并访问http://localhost:8005/dept/msg接口
- 查看sentinel控制台
Sentinel控制台规则配置详解
最合适场景
- Provider 端控制脉冲流量
- 针对不同调用来源进行流控
- Web接口流控
如何配置规则
- 梳理核心接口
- 通过事前压测评估核心接口的容量,配置QPS阈值
- 自定义流控异常
@RestController
@RequestMapping("/dept")
@SuppressWarnings("all")
public class DeptController {
@Value("${server.port}")
private int port;
@GetMapping("/msg")
@SentinelResource(value = "flow",blockHandler = "flowBlockHandler")
public String getMessage(){
return "获取服务成功!"+port;
}
public String flowBlockHandler(BlockException e){
return "流控异常!";
}
}
再次频繁访问http://localhost:8005/dept/msg不会出现流控异常
- 原因:一旦服务重启之后,在sentinel控制台配置的服务信息将丢失,需要
重新设置
或持久化
5.6 Sentinel—并发线程数—流控规则
并发线程数
并发数控制用于保护业务线程不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为了应对太多线程占用的情况,业务有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求就会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置
测试
- 在controller中添加一个接口方法
@GetMapping("/msgThread")
@SentinelResource(value = "msgThread",blockHandler = "flowBlockHandler")
public String getMessageThread() throws InterruptedException {
//睡眠5秒,模拟多线程
TimeUnit.SECONDS.sleep(5);
return "获取服务成功!"+port;
}
- 重新启动该模块springcloud-provider-dept-sentinel-8005
- 访问http://localhost:8005/dept/msgThread,使得在sentinel监控控制台显示出来
- 修改对应资源名的流控规则,如下图所示
- 打开两个浏览器,两者快速访问http://localhost:8005/dept/msgThread
5.7 Sentinel—BlockException统一异常处理
如果不想再每个接口中加上@SentinelResource
注解
可以自定义BlockExceptionHandler的实现类统一处理BlockException
- 编写实现类MyBlockExceptionHandler.class
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
//getRule() 获取规则的详细信息
log.info("BlockExceptionHandler ==========================="+e.getRule());
Result r = null;
if (e instanceof FlowException){
r = Result.error(100,"接口限流了!");
} else if (e instanceof DegradeException){
r = Result.error(101,"服务降级了!");
} else if (e instanceof ParamFlowException){
r = Result.error(102,"热点参数限流了!");
} else if (e instanceof SystemBlockException){
r = Result.error(103,"触发系统保护规则!");
} else if (e instanceof AuthorityException){
r = Result.error(104,"授权规则不通过!");
}
//返回json数据
httpServletResponse.setStatus(500);
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(httpServletResponse.getWriter(),r);
}
}
- 返回结果类Result.class
public class Result<T> {
private Integer code;
private String msg;
private T data;
public Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static Result error(Integer code, String msg){
return new Result(code, msg);
}
}
-
将Controller中的
@SentinelResource
注解去掉 -
访问http://localhost:8005/dept/msg,使得在sentinel监控控制台显示出来
-
修改对应资源名的流控规则,如下图所示
5.8 Sentinel—关联流控模式
流控模式
基于调用关系的流量控制。调用关系包括调用方、被调用方;一个方法可能会调用其它方法,形成一个调用链路的层次关系
- 直接
资源调用达到设置的阈值之后直接被流控抛出异常
- 关联
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如数据库中同一个字段的读操作和写操作存在争抢,读的速度过高会影响写的速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可以使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db
和 write_db
这两个资源分别代表数据库读写,我们可以给 read_db
设置限流规则来达到写的优先目的:设置 strategy
为 RuleConstant.STRATEGY_RELATE
同时设置 refResource
为 write_db
。这样当写库操作过于频繁时,都数据库的请求会被限流
测试
- 在controller中增加两个接口
@RequestMapping("/add")
public String add(){
return "生成订单";
}
@RequestMapping("/query")
public String query(){
return "查询订单";
}
-
分别访问两个接口,使得在sentinel监控控制台显示出来
-
设置关联限流
- 使用JMeter工具持续访问http://localhost:8005/dept/query
5.9 Sentinel—链路流控模式
- 根据调用链路入口限流
- 编写一个DepartmentService接口的实现类
// DepartmentService接口
public interface DepartmentService {
String getDepartment();
}
// DepartmentServiceImpl类
@Service
public class DepartmentServiceImpl implements DepartmentService {
@Override
@SentinelResource(value = "getDept")
public String getDepartment() {
return "获取部门";
}
}
- 分别访问两个接口,使得在sentinel监控控制台显示出来
- 在sentinel控制台进行以下设置
发现该接口没有被监控,原因:sentinel默认将我们的调用链路收敛了
需要在application.yml
中添加配置
server:
port: 8005
spring:
application:
name: springcloud-provider-dept-sentinel # 应用名称
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8858 # 监控地址
# 默认将我们的调用链路收敛了,设置为false取消收敛
web-context-unify: false
- 重新启动,再执行第2步骤
- 测试访问
连续访问:http://localhost:8005/dept/test1
http://localhost:8005/dept/test2
发现连续访问 /dept/test2
会报500错误,因为使用了@SentinelResource
注解,没有在调用的方法注解@SentinelResource
中加上blockHandler=“”,自定义的MyBlockExceptionHandler.class失效了,需要手动添加一个blockHandler方法
- 修改DepartmentServiceImpl.class
@Service
public class DepartmentServiceImpl implements DepartmentService {
@Override
@SentinelResource(value = "getDept", blockHandler = "blockHandlerGetDept")
public String getDepartment() {
return "获取部门";
}
public String blockHandlerGetDept(BlockException e){
return "getDepartment流控异常";
}
}
- 重新启动,再次执行第2步骤
快速访问:http://localhost:8005/dept/test2
快速访问:http://localhost:8005/dept/test1
5.10 流控效果
快速失效
(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水准时
Warm Up(激增流量)
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓缓增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮
冷加载因子:codeFactor默认是3,即请求 QPS 从threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示
- 测试
配置如图所示
5.11 Sentinel—排队等待
匀速排队(脉冲流量)
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法
该方法的作用如下图所示:
这种方式主要用于处理间隔性突发的流量,例如消息队列。假设有这样的情景:在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是第一秒直接拒绝多余的请求
【注意】匀速排队模式暂时不支持 QPS > 1000 的场景
测试
场景一:直接失败
- 给
/dept/msg
接口配置流控
- 使用JMeter进行负载压力测试
可以看到下图,10个线程请求,由于阈值是5,所以每执行一次10个请求,有5个请求是失败的,并且每执行完一次10个请求就会等待5s(由JMeter设置)
场景二:排队等待
- 再次修改接口
/dept/msg
的流控设置
小结
- 在一定流控阈值内,如果在该定量的请求能够及时完成,那么正等待的请求也有机会完成,最主要还是看服务的性能
5.12 Sentinel—熔断降级规则
保护自身的手段(通常在consumer端组合配置)
- 并发控制(信号量隔离)
- 基于慢调用比例熔断
- 基于异常比例熔断
触发熔断后的处理逻辑示例
- 提供 fallback 实现(服务降级)
- 返回错误result
- 读缓存(DB访问降级)
慢调用比例
慢调用比例 (SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断
接口代码
@GetMapping("/msgThread")
// @SentinelResource(value = "msgThread",blockHandler = "flowBlockHandler")
public String getMessageThread() throws InterruptedException {
//睡眠5秒,模拟多线程
TimeUnit.SECONDS.sleep(3);
return "获取服务成功!"+port;
}
- 对接口
/dept/msgThread
配置降级规则
- 在 JMeter 工具进行测试
- 执行 JMeter 测试后,立即访问http://localhost:8005/dept/msgThread
异常比例
异常比例 (ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0]
,代表 0% - 100%
- 在controller中新增一个接口
@RequestMapping("/exception")
public String error(){
int i = 1/0;
return "hello";
}
- 对接口
/dept/exception
配置异常比例降级
- 使用JMeter对接口
/dept/exception
快速访问10次,查看结果树
异常数
5.13 Sentinel—整合Openfeign降级
测试
新建一个模块springcloud-comsuner-dept-openfeign-sentinel-80
- 导入依赖
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--nacos-服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--sentinel依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
- 编写
DeptClientService
接口和DeptClientServiceFallback.class
类
/**
* name 指定调用rest接口所对应的服务名
* path 指定调用rest接口所在的Controller指定的@RequestMapping
*/
@FeignClient(name = "springcloud-provider-dept",path = "/dept",fallback = DeptClientServiceFallback.class)
public interface DeptClientService {
@GetMapping("/msg")
String getMessage();
}
@Component
public class DeptClientServiceFallback implements DeptClientService{
@Override
public String getMessage() {
return "降级了!";
}
}
- 编写控制类
@RestController
@RequestMapping("/consumer")
@SuppressWarnings("all")
public class DeptController {
@Autowired
private DeptClientService clientService;
@RequestMapping("/dept/msg")
public String getMessage(){
return this.clientService.getMessage();
}
}
- 再不开启任何服务端Provider的情况下,启动springcloud-comsuner-dept-openfeign-sentinel-80工程
访问以上controller中的接口
5.14 Sentinel—热点参数流控
热点参数限流
何为热点?热点即是经常访问的数据
常用场景:
- 热点商品访问/操作控制
- 用户/IP 防刷
实现原理:热点淘汰策略(LRU)+Token Bucket 流控
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看作是一种特殊的流量控制,仅对包含热点参数的资源调用生效
单机阈值
情景一
假设参数大部分都是热点参数,需要针对热点参数进行流控,后续额外针对普通的参数值进行流控
情景二
假设大部分值都是普通参数,额外的参数都是热点参数,后续需要对额外的热点参数进行流控
测试
-
在springcloud-comsuner-dept-openfeign-sentinel-80中的controller新增一个接口
/dept/get/{id}
-
修改对应
application.yml
文件
server:
port: 80
spring:
application:
name: springcloud-consumer-dept-feign-sentinel
# 配置nacos
cloud:
nacos:
# nacos 服务地址
server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
discovery:
username: nacos # nacos用户名
password: nacos
namespace: public # 分隔开发环境和测试环境使用
# 不向服务注册中心注册自己
register-enabled: false
# 设置监控服务
sentinel:
transport:
dashboard: 127.0.0.1:8858
# 默认将我们的调用链路收敛了,需要打开
web-context-unify: false
feign:
sentinel:
# openfeign整合sentinel
enabled: true
- 访问该接口,是该接口输出在Sentinel控制台,并对其增加以下配置
4.访问http://localhost/consumer/dept/get/1
- 使用JMeter工具测试http://localhost/consumer/dept/get/2
5.15 Sentinel—系统保护规则
- 容量评估不到位,某个大流量接口限流配置不合理或没有配置,导致系统崩溃,来不及进行处理
- 突然发现机器的 Load 和 CPU usage 等开始飙高,但却没有办法很快的确认到是什么原因造成的,也来不及处理
- 当其中一台机器挂了之后,本该由这台机器处理的流量被负载均衡到另外的机器上,另外的机器也被打挂了,引起系统雪崩
- 希望有个全局的兜底防护,即使却反容量评估也希望有一定的保护机制
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性
- Load 自适应(仅对Linux/Unix-like 机器生效):系统的 load 1 作为启发指标,进行自适应系统保护。当系统 load 1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会出发系统保护(BBR 阶段)。系统容量由
maxQps * minRt
估算得出。设定参考值一般是CPU cores * 2.5
- CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围0.0~1.0),比较灵敏
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护
测试
- 使用springcloud-provider-dept-sentinel-8005模块进行测试
- 访问该工程接口,在Sentinel控制台显示出来
- 查看本机的CPU利用率
- 对应本机CPU利用率对该模块配置系统保护规则,如下图所示
- 任意快速访问该模块的接口
其它配置都如同,只要超出自己在该模块中所配置的系统保护条件,就会触发系统保护规则
5.16 Sentinel—规则持久化
Sentinel规则的推送有下面三种模式
推送模式 | 说明 | 优点 | 缺点 |
---|---|---|---|
原始模式 | API将规则推送至客户端并直接更新到内存中,扩展写数据源(WrtiableDataSource) | 简单,无任何依赖 | 不保证一致性;规则存在内存中,重启即消失。那个不建议用于生产环境 |
Pull 模式 | 扩展写数据源(WrtiableDataSource),客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心是 RDBMS、文件等 | 无任何依赖,规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题 |
Push 模式 | 扩展读数据源(ReadableDataSouce),规则中心同意推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper等配置中心,这种方式有更好的实时性和一致性保证。生产环境下一般采用push模式的数据源 | 规则持久化;一致性;快速 | 引入第三方依赖 |
原始模式
如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中
这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境
pull 模式
pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistery
中
push 模式
生产环境一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(Zookeeper、Nacos、Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 —> 配置中心 —> Sentinel 数据源 —> Sentinel。而不是经 Sentinel 数据源推送至配置中心
流量控制
概述
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性
FlowSlot
会根据预设的规则,结合前面 NodeSelectorSlot
、ClusterBuilderSlot
、StatisticSlot
统计出来的实时信息进行流量控制
限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName)
的时候抛出 FlowException
异常。FlowException
是 BlockException
的子类,您可以捕捉 BlockException
来自定义被限流之后的处理逻辑
同一个资源可以创建多条限流规则。FlowSlot
会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
resource
:资源名,即限流规则的作用对象(接口)count
: 限流阈值grade
: 限流阈值类型(QPS 或并发线程数,QPS 模式(1)或并发线程数模式(0))limitApp
: 流控针对的调用来源,若为default
则不区分调用来源strategy
: 调用关系限流策略(直接、链路、关联)controlBehavior
: 流量控制效果(直接拒绝、Warm Up、匀速排队)
测试
- 在springcloud-provider-dept-sentinel-8005模块下
- 在Linux启动nacos-7070
- 访问http://192.168.159.100:7070/nacos/index.html#,新建一个配置文件,如下图所示
- 修改对应
application.yml
server:
port: 8005
spring:
application:
name: springcloud-provider-dept-sentinel # 应用名称
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8858 # 监控地址
# 默认将我们的调用链路收敛了,需要打开
web-context-unify: false
datasource:
flow-rule: # 数据源可以自定义配置
nacos:
server-addr: 192.168.159.100:7070
# 由于在Linux开启了权限,需要设置用户名、密码
username: nacos
password: nacos
# nacos配置文件的dataId
dataId: springcloud-provider-dept-sentinel-flow-rule
# 规则类型选择
rule-type: flow
FLOW("flow", FlowRule.class),
DEGRADE("degrade", DegradeRule.class),
PARAM_FLOW("param-flow", ParamFlowRule.class),
SYSTEM("system", SystemRule.class),
AUTHORITY("authority", AuthorityRule.class),
GW_FLOW("gw-flow", "com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
GW_API_GROUP("gw-api-group", "com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
- 启动springcloud-provider-dept-sentinel-8005,并访问
/dept/msg
接口,使得该接口在 Sentinel 控制台输出 - 查看 Sentinel 控制台中的流控规则
缺点:
在nacos配置文件中,虽然已经对访问资源进行了持久化,但是要想在 Sentinel 控制台对该资源进行修改,并且同步到Nacos注册中心中的配置文件是不可实现的,需要进行拓展
下一篇文章《微服务分布式组件—Seata》