SpringCloud Alibaba-Sentinel
- 1. Sentinel核心库
- 1.1 Sentinel介绍
- 1.2 Sentinel核心功能
- 1.2.1 流量控制
- 1.2.2 熔断降级
- 2 Sentinel 限流熔断降级
- 2.1 @SentinelResource定义资源
- 2.1.1 blockHandler/blockHandlerClass
- 2.1.2 fallback/fallbackClass
- 2.1.3 defaultFallback
- 2.2 Sentinel的规则
- 2.2.1 流量控制规则 (FlowRule)
- 2.2.1.1 QPS流量控制
- 2.2.1.2 线程数流量控制
- 2.2.2 熔断降级规则 (DegradeRule)
- 2.2.3 系统保护规则 (SystemRule)
- 2.2.4 访问控制规则 (AuthorityRule)
- 2.2.5 热点规则 (ParamFlowRule)
- 2.3 OpenFeign支持
- 2.3.1 fallback
- 2.3.2 fallbackFactory
- 3 Sentinel集成Gateway
- 3.1 Sentinel对网关支持
- 3.2 GateWay集成Sentinel
- 4 Sentinel控制台
- 4.1 Sentinel控制台安装
- 4.2 接入控制台
- 4.3 可视化管理
- 4.3.1 实时监控
- 4.3.2 流控规则
- 4.3.3 降级规则
- 4.3.4 热点数据
1. Sentinel核心库
Sentinel 主页
1.1 Sentinel介绍
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
- Sentinel核心组件
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持
- 控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等
- 同组件功能对比
参看 官方网站
对比内容 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 信号量隔离基于 QPS,支持基于调用关系的限流 | 不支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
多语言支持 | Java/Go/C++ | Java |
- Sentinel基本概念
- 资源
资源是 Sentinel 的关键概念。资源,可以是一个方法、一段代码、由应用提供的接口,或者由应用调用其它应用的接口 - 规则
围绕资源的实时状态设定的规则,包括流量控制规则、熔断降级规则以及系统保护规则、自定义规则 - 降级
在流量剧增的情况下,为保证系统能够正常运行,根据资源的实时状态、访问流量以及系统负载有策略的拒绝掉一部分流量 - 熔断
一个轻轻过来, 可能服务调用的链路长, 在某个服务发生问题时可能会造成服务雪崩 如果触发熔断机制,则可以保证后续的请求不会继续发送到目标服务上
降级和熔断的区别是, 降级为可用, 熔断为不可用
1.2 Sentinel核心功能
1.2.1 流量控制
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系
- 运行指标,例如 QPS、线程池、系统负载等
- 控制的效果,例如直接限流、冷启动、排队等
1.2.2 熔断降级
除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误
Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障
Sentinel熔断降级设计:
Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配
Sentinel熔断降级设计:
- 并发线程数限制:和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要用户预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求
- 响应时间降级:除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
系统自适应保护:
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
2 Sentinel 限流熔断降级
Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果
以下内容根据上面服务架构进行熔断限流操作
使用Spring Cloud Alibaba Sentinel 使用手册
首先需要引入spring-cloud-starter-alibaba-sentinel依赖,并使用@SentinelResource标识资源
在hailtaxi-driver工程中引入spring-cloud-starter-alibaba-sentinel依赖,依赖如下:
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
2.1 @SentinelResource定义资源
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性: 具体参考
value | 资源名称,必需项(不能为空) |
---|---|
blockHandler / blockHandlerClass | blockHandler 对应处理 BlockException 的函数名称,可选项
|
fallback / fallbackClass | fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
|
defaultFallback(1.6.0 开始) | 默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
|
exceptionsToIgnore(1.6.0 开始) | 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出 |
entryType | entry 类型,可选项(默认为 EntryType.OUT) |
2.1.1 blockHandler/blockHandlerClass
制造SystemBlockException异常
@SentinelResource(value = "info",blockHandler = "blockExHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
log.info("当前服务占用的端口为:{}",port);
Driver driver = driverService.findById(id);
if (driver==null) {
//throw new RuntimeException("司机id="+id+"不存在");
throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
}
return driver;
}
/**
* info资源出现BlockException后的降级处理
*/
public Driver blockExHandler(String id,BlockException e) {
Driver driver = new Driver();
driver.setId(id);
driver.setName("系统繁忙,稍后再试");
return driver;
}
指定blockHandlerClass
指定的class必须要是静态方法
@SentinelResource(value = "info",blockHandler = "blockExHandler",blockHandlerClass = "xxx.xxx.Xxxx")
注意:这种方式只能拦截BlockException异常, 局限性比较高
2.1.2 fallback/fallbackClass
如果希望抛出任何异常都能处理,都能调用默认处理方法,而并非只是BlockException异常才调用,此时可以使用@SentinelResource的fallback属性,代码如下:
@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
log.info("当前服务占用的端口为:{}",port);
Driver driver = driverService.findById(id);
if (driver==null) {
throw new RuntimeException("司机id="+id+"不存在");
// throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
}
return driver;
}
/**
* info资源出现任何类型异常后的降级处理
* 方法参数可以添加一个Throwable 类型的参数,也可不添加
*/
public Driver exHandler(String id,Throwable e) {
Driver driver = new Driver();
driver.setId(id);
driver.setName("系统繁忙,稍后再试");
return driver;
}
指定fallbackClass
指定的class必须要是静态方法
@SentinelResource(value = "info",fallback ="exHandler" ,fallbackClass = "xx.xxx.xxx.xx.Xxx")
2.1.3 defaultFallback
上面无论是blockHandler还是fallback,每个方法发生异常,都要为方法独立创建一个处理异常的方法,效率非常低,可以使用@SentinelResource注解的defaultFallback属性,为一个类指定一个全局的处理错误的方法,代码如下:
@RestController
@RequestMapping(value = "/driver")
@Slf4j
@RefreshScope
@SentinelResource(defaultFallback = "defaultExHandler")
public class DriverController {
@Autowired
private DriverService driverService;
public Driver defaultExHandler(Throwable e) {
Driver driver = new Driver();
driver.setName("系统繁忙,稍后再试");
return driver;
}
/****
* 司机信息
*/
//@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
@SentinelResource("info")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
log.info("当前服务占用的端口为:{}",port);
Driver driver = driverService.findById(id);
if (driver==null) {
throw new RuntimeException("司机id="+id+"不存在");
// throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
}
return driver;
}
两个字: 也很麻烦
不过一般都不会这么写, 通常会和OpenFeign的FallbackFactory结合使用
2.2 Sentinel的规则
参考官网
Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供用户来定制自己的规则策略
Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则
2.2.1 流量控制规则 (FlowRule)
参看 链接1、链接2
流量规则的定义,重要属性如下:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 |
limitApp | 流控针对的调用来源 | default,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流 | 直接拒绝 |
clusterMode | 是否集群限流 | 否 |
同一个资源可以同时有多个限流规则,检查规则时会依次检查
strategy限流策略说明:
- 直接: 资源达到限流条件时,直接限流
- 关联: A资源关联B资源,当关联的B资源达到阈值限流时,A资源也会被限流
- 链路: 对于某资源C,有两个入口,从资源A->C,从资源B->C, 通过指定入口资源可以达到只记录从该入口进来的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以对其限流)
controlBehavior流控说明:
- 直接拒绝: 请求直接失败
- WarmUp: 当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮
- 排队等待: 排队处理请求
2.2.1.1 QPS流量控制
Application初始化完成之后加载规则
/***
* 初始化规则
*/
@PostConstruct
private void initFlowRule() {
//规则集合
List<FlowRule> rules = new ArrayList<FlowRule>();
//定义一个规则
FlowRule rule = new FlowRule("info");
// 设置阈值
rule.setCount(2);
//设置限流阈值类型
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//default,代表不区分调用来源
rule.setLimitApp("default");
//设置流控效果 当QPS超过任意规则的阈值后,新的请求就会被立即拒绝
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
//将定义的规则添加到集合中
rules.add(rule);
//加载规则
FlowRuleManager.loadRules(rules);
}
2.2.1.2 线程数流量控制
修改限流阈值类型,代码如下:
@PostConstruct
public void initFlowRule() {
//规则集合
List<FlowRule> rules = new ArrayList<>();
// 定义一个规则
FlowRule rule = new FlowRule("info");
// 设置基流量控制 的类型
rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);//默认是qps
//设置流量阈值
rule.setCount(2);
// 将 规则添加到 集合中
rules.add(rule);
// 加载规则
FlowRuleManager.loadRules(rules);
}
此时再来访问http://localhost:18081/driver/info/1 无论怎么访问都不会出现降级现象,但是如果用Jmeter模拟多个线程,会执行降级方法,效果如下:
2.2.2 熔断降级规则 (DegradeRule)
参看 链接1、链接2
熔断降级规则包含下面几个重要的属性:
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() 方法来用硬编码的方式定义熔断规则
@PostConstruct
public void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
// 设置资源名称
rule.setResource("info");
/**
* 设置熔断策略
* DEGRADE_GRADE_RT:平均响应时间
* DEGRADE_GRADE_EXCEPTION_RATIO:异常比例数量
* DEGRADE_GRADE_EXCEPTION_COUNT:异常数
*/
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
//设置阈值
rule.setCount(2);
//设置 熔断时长(单位秒)
rule.setTimeWindow(30);
// 统计时长(单位为 ms) 默认1000
rule.setStatIntervalMs(60*1000);
//将规则添加到集合中
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
上面设置的规则的意思是, 在1分钟内请求出现异常的数量超过2次,服务熔断降级, 30秒后回复正常
2.2.3 系统保护规则 (SystemRule)
参考: 链接1、链接2
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.9);
//所有入口资源的 QPS,-1 (不生效)
rule.setQps(10);
//入口流量的最大并发数,-1 (不生效)
rule.setMaxThread(5);
//所有入口流量的平均响应时间,单位:毫秒,-1 (不生效)
rule.setAvgRt(1000);
//load1 触发值,用于触发自适应控制阶段,系统最高负载,建议取值 CPU cores * 2.5
rule.setHighestSystemLoad(20);
//将规则加入到集合
rules.add(rule);
SystemRuleManager.loadRules(rules);
}
可以测试CPU使用率自我保护,使用jmeter测试如下
2.2.4 访问控制规则 (AuthorityRule)
参看:链接1、链接2
很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
授权规则,即黑白名单规则(AuthorityRule)非常简单,主要有以下配置项:
- resource:资源名,即规则的作用对象
- limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB
- strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式
可以通过AuthorityRuleManager.loadRules来加载规则
@PostConstruct
public void initAuthorityRule() {
AuthorityRule rule = new AuthorityRule();
rule.setResource("info");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("127.0.0.1,demo.com");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));
}
/**
* Sentinel提供了 RequestOriginParser 接口来处理访问来源,Sentinel保护的资源如果被访问,
* 就会调用 RequestOriginParser解析访问来源
*/
@Component
public class IpLimiter implements RequestOriginParser{
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
return httpServletRequest.getRemoteAddr();
}
}
2.2.5 热点规则 (ParamFlowRule)
参看:飞一个
何为热点?热点即经常访问的数据。
很多时候我们希望统计某个热点数据中访问频次最高的 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 | 集群流控相关配置 |
可以通过 ParamFlowRuleManager 的 loadRules 方法更新热点参数规则
@SentinelResource(value = "search")
@GetMapping(value = "/search/{city}")
public Driver search(@PathVariable(value = "city")String city){
System.out.println("查询的司机所在城市:"+city);
//假设查询到了一个司机信息
Driver driver = new Driver();
driver.setName("A");
driver.setId("1");
return driver;
}
/***
* 热点参数初始化
*/
@PostConstruct
private static void initParamFlowRules() {
ParamFlowRule rule = new ParamFlowRule("search")
//参数下标为0
.setParamIdx(0)
//限流模式为QPS
.setGrade(RuleConstant.FLOW_GRADE_QPS)
//统计窗口时间长度(单位为秒)默认1秒
.setDurationInSec(10)
//流控效果(支持快速失败和匀速排队模式)
//CONTROL_BEHAVIOR_DEFAULT:限流行为,直接拒绝
//CONTROL_BEHAVIOR_WARM_UP:限流行为,匀速排队
//CONTROL_BEHAVIOR_RATE_LIMITER:限流行为,匀速排队
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
//最大排队等待时长(仅在匀速排队模式生效 CONTROL_BEHAVIOR_RATE_LIMITER)
//.setMaxQueueingTimeMs(600)
//最大阈值为5
.setCount(5);
// 为特定参数单独设置规则
//如下配置:当参数值为恩施的时候,阈值到达2的时候则执行限流
ParamFlowItem item = new ParamFlowItem()
//参数类型为String类型
.setClassType(String.class.getName())
//设置阈值为2
.setCount(2)
//需要统计的值
.setObject(String.valueOf("恩施"));
rule.setParamFlowItemList(Collections.singletonList(item)); //返回的是不可变的集合,但是这个长度的集合只有1,可以减少内存空间
//加载热点数据
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}
非热点参数5次限流, 热点参数2次限流
以上是被调用放的熔断降级, 下面聊聊调用方的
2.3 OpenFeign支持
参看:链接
Sentinel 适配了 Feign 组件。如果想使用,除了外还需要 2 个步骤:
1:引入
spring-cloud-starter-alibaba-sentinel
的依赖
3:配置文件打开 Sentinel 对 Feign 的支持:feign.sentinel.enabled=true
- 引入sentinel和OpenFeign依赖,配置如下:
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
- 配置文件中开启Feign支持sentinel,配置如下:
feign:
#开启Sentinel对Feign的支持
sentinel:
enabled: true
不打开默认使用hytrix, 而且hytrix默认是打开的
2.3.1 fallback
- 为Feign接口创建一个实现类:DriverFeignFallback,在实现类中处理程序异常降级处理方法,代码如下:
@Component
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;
}
}
- 在DriverFeign接口上添加fallback属性指定降级处理的类,代码如下:
@FeignClient(value = "hailtaxi-driver",fallback = DriverFeignFallback.class)
2.3.2 fallbackFactory
可以为DriverFeign接口创建一个降级处理的工厂对象,在工厂对象中处理程序异常降级处理方法,用工厂对象处理可以拿到异常信息,代码如下:
- 创建DriverFeignFallbackFactory
@Component
public class DriverFeignFallbackFactory 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;
}
};
}
}
2、在DriverFeign接口上添加fallbackFactory属性指定讲解处理的类,代码如下:
@FeignClient(value = "hailtaxi-driver",fallbackFactory = DriverFeignFallbackFactory.class)
3 Sentinel集成Gateway
参看:再飞一个
我们的项目流量入口是SpringCloud Gateway,因此重点看Sentinel集成Gateway
3.1 Sentinel对网关支持
Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流
Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
- route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
- 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 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) 注册动态规则源动态推送(推荐方式)
3.2 GateWay集成Sentinel
参考:Sentinel · alibaba/spring-cloud-alibaba Wiki · GitHub
- 引入如下依赖:
<!--Sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
- 创建配置类GatewayConfiguration
自版本2.2.0起,不再需要手动注入SentinelGatewayBlockExceptionHandler和SentinelGatewayFilter
@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();
}
@PostConstruct
public void doInit() {
// 自定义 api 分组
initCustomizedApis();
// 初始化网关流控规则
initGatewayRules();
}
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("customer_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/order/**")
/**
* 匹配策略:
* URL_MATCH_STRATEGY_EXACT:url精确匹配
* URL_MATCH_STRATEGY_PREFIX:url前缀匹配
* URL_MATCH_STRATEGY_REGEX:url正则匹配
*/
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(api1);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("hailtaxi-driver") // 资源名称,可以是网关中的 routeid或者用户自定义的 API分组名称
.setCount(2) // 限流阈值
.setIntervalSec(10) // 统计时间窗口默认1s
.setGrade(RuleConstant.FLOW_GRADE_QPS) // 限流模式
/**
* 限流行为:
* CONTROL_BEHAVIOR_RATE_LIMITER 匀速排队
* CONTROL_BEHAVIOR_DEFAULT 快速失败(默认)
* CONTROL_BEHAVIOR_WARM_UP:
* CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
*/
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
//匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效
.setMaxQueueingTimeoutMs(1000)
/**
* 热点参数限流配置
* 若不设置,该网关规则将会被转换成普通流控规则;否则会转换成热点规则
*/
.setParamItem(new GatewayParamFlowItem()
/**
* 从请求中提取参数的策略:
* PARAM_PARSE_STRATEGY_CLIENT_IP
* PARAM_PARSE_STRATEGY_HOST
* PARAM_PARSE_STRATEGY_HEADER
* PARAM_PARSE_STRATEGY_URL_PARAM
*/
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
/**
* 若提取策略选择 Header 模式或 URL 参数模式,
* 则需要指定对应的 header 名称或 URL 参数名称。
*/
.setFieldName("token")
/**
* 参数的匹配策略:
* PARAM_MATCH_STRATEGY_EXACT
* PARAM_MATCH_STRATEGY_PREFIX
* PARAM_MATCH_STRATEGY_REGEX
* PARAM_MATCH_STRATEGY_CONTAINS
*/
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
//参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控
.setPattern("123456") // token=123456 10s内qps达到2次会被限流
)
);
rules.add(new GatewayFlowRule("customer_api")
/**
* 规则是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)
* 还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME),默认是 route。
*/
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(2)
.setIntervalSec(1)
.setGrade(RuleConstant.FLOW_GRADE_QPS)
);
GatewayRuleManager.loadRules(rules);
}
}
4 Sentinel控制台
查看:官方文档
Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效果
4.1 Sentinel控制台安装
Sentinel 控制台包含如下功能:
- 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
- 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
规则管理和推送:统一管理推送规则。- 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。
Sentinel控制台安装可以基于jar包启动的方式安装,也可以基于docker安装
docker run --name=sentinel-dashboard -d -p 8858:8858 -d --restart=on-failure bladex/sentinel-dashboard:1.8.0
登录后,效果如下:
4.2 接入控制台
- 引入依赖包:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: 192.168.200.200:8858
4.3 可视化管理
4.3.1 实时监控
同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在"实时监控"下。
注意: 实时监控仅存储 5 分钟以内的数据,如果需要持久化,需要通过调用实时监控接口来定制。
4.3.2 流控规则
可以在【流控规则】页面中新增,点击【流控规则】进入页面新增页面,如下图:
4.3.3 降级规则
降级规则的熔断策略有3种,分别是慢调用比例、异常比例、异常数
4.3.4 热点数据
热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制
拓展知识点:
注意:如果流控模式是链路模式,需要引入如下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>1.8.0</version>
</dependency>
创建过滤器:
/***
* CommonFilter过滤器
* @return
*/
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*"); //过滤所有请求
// 入口资源关闭聚合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
bootstrap.yml配置:web-context-unify: false
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8858
web-context-unify: false #可根据不同的URL 进行链路限流