Sentinel实战应用
# 1 Sentinel核心库
Sentinel主页 https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5 (opens new window)
# 1.1 Sentinel介绍
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
1)Sentinel核心组件
1:核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
2:控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。
2
3
4
2)同组件功能对比
对比内容 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 不支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
3)Sentinel基本概念
- 资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
2
3
4
- 规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
# 1.2 Sentinel核心功能
# 1.2.1 流量控制
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
# 1.2.2 熔断降级
1)什么是熔断降级
除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。
Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
2)Sentinel熔断降级设计
Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。
Sentinel熔断降级设计:
并发线程数限制:和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
响应时间降级:除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
2
3
4
3)系统自适应保护
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
# 2 Sentinel 限流熔断降级
Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果。我们先来学习Sentinel 核心库的使用,后面再学习Dashboard使用。
在我们项目中,用户请求通过hailtaxi-gateway
路由到hailtaxi-driver
或者hailtaxi-order
,还有可能在hailtaxi-order
中使用feign调用hailtaxi-driver
,所以我们有可能在单个服务中实现熔断限流,也有可能要集成feign调用实现熔断限流,还有可能在微服务网关中实现熔断限流。我们接下来一步一步实现每一种熔断限流操作。
使用Spring Cloud Alibaba Sentinel
使用手册:https://github.com/alibaba/spring-cloud-alibaba/wiki/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.5.RELEASE</version>
</dependency>
2
3
4
5
6
# 2.1 @SentinelResource定义资源
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource
注解包含以下属性:
具体参考:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81 (opens new window)
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/blockHandlerClass
在hailtaxi-driver
中找到DriverController
中的info
方法,用户在打车的时候,会查询司机信息,如果司机不存在,此时会报错,代码改造如下:
/****
* 司机信息
*/
@GetMapping(value = "/info/{id}")
//@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id){ // 去掉参数上的HttpServletRequest request
log.info("当前服务占用的端口为:{}",port);
Driver driver = driverService.findById(id);
if (driver==null) {
throw new RuntimeException("司机id="+id+"不存在");
}
return driver;
}
2
3
4
5
6
7
8
9
10
11
12
13
如果此时访问:http://localhost:18081/driver/info/3 查询司机信息,如果没有ID为3的司机信息,会报如下错误,
这种体验非常差,我们可以集成Sentinel使用@SentinelResource
的blockHandler
返回默认错误信息,形成降级!!!
1、Sentinel 支持在程序中抛出它定义的BlockException
异常,该异常会被Sentinel捕获,然后走降级方法,
为info()
方法添加一个@SentinelResource
注解,用来标注资源,表示当前方法需要执行限流、降级,在注解中添加value属性,用来标注资源,说白了就是给当前资源起个名字,blockHandler用来表示当前方法发生BlockException
异常的时候,将处理流程交给指定的方法blockExHandler()
处理,此时blockExHandler()
方法必须和抛出异常的方法在同一个类中,这是一种降级操作,代码如下:
/****
* 司机信息
*/
@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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
注意:
如果blockHandler方法和资源方法不在同一个类中,我们可以在
@SentinelResource
中添加blockHandlerClass
属性,指定降级处理类的方法所在的类,且要求blockHandler方法是静态的,代码如下:@SentinelResource(value = "info",blockHandler = "blockExHandler",blockHandlerClass = "xxx.xxx.Xxxx")
1
2、启动测试,访问:http://localhost:18081/driver/info/3 测试出错效果如下:
# fallback/fallbackClass
1、如果我们希望抛出任何异常都能处理,都能调用默认处理方法,而并非只是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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
注意:
如果fallback方法和当前的资源方法不在同一个类中,可以使用
@SentinelResource
注解的fallbackClass
实现,也要求fallback方法是静态的,代码如下:@SentinelResource(value = "info",fallback ="exHandler" ,fallbackClass = "xx.xxx.xxx.xx.Xxx")
1
2、访问 http://localhost:18081/driver/info/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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
访问 http://localhost:18081/driver/info/3 效果如下:
# 2.2 Sentinel的规则
Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略。
Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则。
# 2.2.1 流量控制规则 (FlowRule)
查看:
https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6 (opens new window)
流量规则的定义,重要属性如下:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 |
limitApp | 流控针对的调用来源 | default ,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流 | 直接拒绝 |
clusterMode | 是否集群限流 | 否 |
同一个资源可以同时有多个限流规则,检查规则时会依次检查
strategy限流策略说明:
直接:资源达到限流条件时,直接限流。 关联:A资源关联B资源,当关联的B资源达到阈值限流时,A资源也会被限流。 链路:对于某资源C,有两个入口,从资源A->C,从资源B->C, 通过指定入口资源可以达到只记录从该入口进来的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以对其限流)。
1
2
3
4
5
6
7controlBehavior流控说明:
直接拒绝:请求直接失败。 WarmUp:当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。 排队等待:排队处理请求。
1
2
3
理解上面规则的定义之后,我们可以通过调用 FlowRuleManager.loadRules()
方法来用硬编码的方式定义流量控制规则。
# QPS流量控制
1、我们先实现基于QPS流量控制,在hailtaxi-driver
的DriverApplication
启动类上添加如下方法加载限流规则,当DriverApplication
初始化完成之后加载规则,代码如下:
/***
* 初始化规则
*/
@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");
//设置流控效果
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
//将定义的规则添加到集合中
rules.add(rule);
//加载规则
FlowRuleManager.loadRules(rules);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2、访问 http://localhost:18081/driver/info/1 此时不会抛出异常,但是频繁刷新,则会调用降级方法,效果如下:
# 线程数流量控制
1、我们修改限流阈值类型,代码如下:
@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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2、此时再来访问http://localhost:18081/driver/info/1
我们发现用浏览器无论怎么访问都不会出现降级现象,但是如果用Jmeter模拟多个线程,效果就不一样了,效果如下:
# 2.2.2 熔断降级规则 (DegradeRule)
参看:
https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7 (opens new window)
熔断降级规则包含下面几个重要的属性:
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()
方法来用硬编码的方式定义熔断规则,
1、在DriverApplication
规则定义如下:
@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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2、首先访问:http://localhost:18081/driver/info/1 ,确保没问题,
其次访问:http://localhost:18081/driver/info/3,多访问几次,造成熔断(5+2=7)
然后在访问:http://localhost:18081/driver/info/1,会发现已经有熔断降级效果了,
且查看服务控制台,发现不会有信息输出,表明已经熔断了,且从页面展示效果来看走了降级
等待30s后,再次访问:http://localhost:18081/driver/info/1,查看熔断是否结束!
# 2.2.3 系统保护规则 (SystemRule)
参考:
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()
方法来用硬编码的方式定义流量控制规则。
1、在hailtaxi-driver
的DriverApplication
中创建如下方法,代码如下:
/***
* 系统自我保护
*/
@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(1000);
//load1 触发值,用于触发自适应控制阶段,系统最高负载,建议取值 CPU cores * 2.5
rule.setHighestSystemLoad(20);
//将规则加入到集合
rules.add(rule);
SystemRuleManager.loadRules(rules);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
我们可以测试CPU使用率自我保护,使用jmeter
测试如下:
# 2.2.4 访问控制规则 (AuthorityRule)
参看:
很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
授权规则,即黑白名单规则(AuthorityRule
)非常简单,主要有以下配置项:
resource
:资源名,即规则的作用对象limitApp
:对应的黑名单/白名单,不同 origin 用,
分隔,如appA,appB
strategy
:限制模式,AUTHORITY_WHITE
为白名单模式,AUTHORITY_BLACK
为黑名单模式,默认为白名单模式
了解了以上规则后,可以通过AuthorityRuleManager.loadRules
来加载规则
1、在hailtaxi-driver
的DriverApplication
中创建如下方法,代码如下:
@PostConstruct
public void initAuthorityRule() {
AuthorityRule rule = new AuthorityRule();
rule.setResource("info");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("127.0.0.1,appB");
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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2、访问:http://localhost:18081/driver/info/1,不通过,走了降级
访问:http://127.0.0.1:18081/driver/info/1
# 2.2.5 热点规则 (ParamFlowRule)
何为热点?热点即经常访问的数据。
很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
1:商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
2:用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
2
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
要使用热点参数限流功能,需要引入以下依赖,将该依赖加入到hailtaxi-driver
中:
<!--热点参数限流-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.8.1</version>
</dependency>
2
3
4
5
6
然后为对应的资源配置热点参数限流规则,并在 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
方法更新热点参数规则
1、在DriverController
中创建一个司机筛选方法,比如根据城市来筛选,在DriverController
中创建一个方法:
/***
* 搜素指定城市的司机
*/
@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("唐僧老师");
driver.setId("No.1");
return driver;
}
2
3
4
5
6
7
8
9
10
11
12
13
2、对热门参数进行控制,对热点数据执行特殊限流,比如资源参数列表中的第一个参数值为恩施
的时候执行限流,在DriverApplication
中创建限流配置,代码如下:
/***
* 热点参数初始化
*/
@PostConstruct
private static void initParamFlowRules() {
ParamFlowRule rule = new ParamFlowRule("search")
//参数下标为0
.setParamIdx(0)
//限流模式为QPS
.setGrade(RuleConstant.FLOW_GRADE_QPS)
//统计窗口时间长度(单位为秒)
.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));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2、我们访问 http://localhost:18081/driver/search/天津/ 的时候,连续执行5次,才会限流,
我们访问 http://localhost:18081/driver/search/恩施/ 的时候,连续执行2次,就会限流,
# 2.3 OpenFeign支持
参看:
Sentinel 适配了 Feign (opens new window) 组件。如果想使用,除了外还需要 2 个步骤:
1:引入 `spring-cloud-starter-alibaba-sentinel` 的依赖
2:加入 spring-cloud-starter-openfeign 依赖
3:配置文件打开 Sentinel 对 Feign 的支持:feign.sentinel.enabled=true
2
3
在上面案例中,我们可以实现用户打车成功调用hailtaxi-order
执行下单,并且通过feign调用hailtaxi-driver
修改司机状态,此时我们可以使用Sentinel实现Feign调用降级、限流。
注意:
在进行操作之前,可以先将
hailtaxi-driver
中的访问控制规则注释掉,注释掉DriverApplication#initAuthorityRule
,DriverApplication#initSystemRule
上的注解即可
1、在hailtaxi-order
中引入sentinel
和OpenFeign
依赖,配置如下:
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!--Openfeign api模块中已存在也可不引入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
2、在hailtaxi-order
的配置文件中开启Feign支持sentinel
,配置如下:
feign:
#开启Sentinel对Feign的支持
sentinel:
enabled: true
2
3
4
注意:现在配置信息都存放在了
nacos
中,所以找到hailtaxi-order.yaml
的Data ID配置,将以上配置添加进去!
注意修改完后一定要发布才能生效!!!
3、因为hailtaxi-order
要通过openfeign
去调用hailtaxi-driver
中的DriverController#status
方法,改造一下该方法
/****
* 更新司机信息
*/
@PutMapping(value = "/status/{id}/{status}")
public Driver status(@PathVariable(value = "id")String id,@PathVariable(value = "status")Integer status) throws Exception {
log.info("当前服务占用的端口为:{}",port);
//修改状态
driverService.update(id,status);
//修改状态后的司机信息
Driver driver = driverService.findById(id);
if (driver == null) {
throw new RuntimeException("学生id="+id+",不存在");
}
return driver;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
模拟被调用服务出现异常的情况
3、先验证正确性,启动hailtaxi-gateway
,hailtaxi-order
,hailtaxi-driver
服务,使用postman访问:
http://localhost:8001/order
4、为了测试程序异常能实现降级操作,我们在hailtaxi-order
中将OrderInfoController.add()
方法的司机ID改成一个不存在的司机ID,让程序报错,测试降级处理,代码如下:
/***
* 下单
*/
@PostMapping
public OrderInfo add(){
//修改司机信息 司机ID=1
Driver driver = driverFeign.status("3",2);// 改成一个不存在的id
//创建订单
OrderInfo orderInfo = new OrderInfo("No"+((int)(Math.random()*10000)), (int)(Math.random()*100), new Date(), "深圳北站", "罗湖港", driver);
orderInfoService.add(orderInfo);
return orderInfo;
}
2
3
4
5
6
7
8
9
10
11
12
5、再次启动测试:
注意:服务调用出错要进行降级操作有两个地方都可以进行,一是在被调用方进行降级,一是在调用方进行降级,
在被调用方进行降级前面已经讲过了,所以接下来讲解如何在调用方(openfeign)结合 sentinel 进行降级处理。
# 2.3.1 fallback
在hailtaxi-api
模块中找到DriverFeign
接口:
1、为Feign接口创建一个实现类:com.itheima.driver.feign.fallback.DriverFeignFallback
,在实现类中处理程序异常降级处理方法,代码如下:
package com.itheima.driver.feign.fallback;
import com.itheima.driver.feign.DriverFeign;
import com.itheima.driver.model.Driver;
import org.springframework.stereotype.Component;
@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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2、在DriverFeign
接口上添加fallback
属性指定降级处理的类,代码如下:
@FeignClient(value = "hailtaxi-driver",fallback = DriverFeignFallback.class)
注意:修改了
hailtaxi-api
模块,最好clean
,package
!!
3、启动运行,会发生如下问题:
java.lang.AbstractMethodError: com.alibaba.cloud.sentinel.feign.SentinelContractHolder.parseAndValidatateMetadata(Ljava/lang/Class;)Ljava/util/List;
出现上面问题的主要原因是当前SpringCloud版本存在问题。
Hoxton.SR1` 中,`fegin.context`接口方法的定义为`parseAndValidatateMetadata` `Hoxton.SR3` 中,`fegin.context`接口方法的定义为`parseAndValidateMetadata
我们现在需要把Hoxton.SR1
换成Hoxton.SR3
,因此需要在hailtaxi-parent
修改SpringCloud版本:
将SpringCloud
版本升级至Hoxton.SR3
同时将SpringBoot
版本升级至2.2.10
,如上图:
https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-Hoxton-Release-Notes
此时我们测试,效果如下:
# 2.3.2 fallbackFactory
我们可以为DriverFeign
接口创建一个降级处理的工厂对象,在工厂对象中处理程序异常降级处理方法,用工厂对象处理可以拿到异常信息,代码如下:
1、创建:com.itheima.driver.feign.fallback.DriverFeignFallbackFactory
package com.itheima.driver.feign.fallback;
import com.itheima.driver.feign.DriverFeign;
import com.itheima.driver.model.Driver;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
@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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2、在DriverFeign
接口上添加fallbackFactory
属性指定讲解处理的类,代码如下:
@FeignClient(value = "hailtaxi-driver",fallbackFactory = DriverFeignFallbackFactory.class)
3、再次启动测试,效果如下:
# 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
spring: gateway: #路由配置 routes: #唯一标识符 - id: hailtaxi-driver uri: lb://hailtaxi-driver #路由断言 predicates: - Path=/driver/** #唯一标识符 - id: hailtaxi-order uri: lb://hailtaxi-order #路由断言 predicates: - Path=/order/**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16自动将每个路由标识为资源,
自定义 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,需要引入依赖包,使用时只需注入对应的 SentinelGatewayFilter
实例以及 SentinelGatewayBlockExceptionHandler
实例即可。
1、首先在hailtaxi-gateway
中引入如下依赖:
<!--Sentinel-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.8.0</version>
</dependency>
2
3
4
5
6
2、实例引入:创建配置类com.itheima.config.GatewayConfiguration
:
package com.itheima.config;
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
此时集成就完成了。
3、启动hailtaxi-gateway
,hailtaxi-drvier
,hailtaxi-order
测试:
使用postman测试,
请求:http://localhost:8001/driver/info/1
10秒内请求超过2次会被限流
请求:localhost:8001/order
1秒内qps达到2次会被限流
# 4 Sentinel控制台
查看:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0 (opens new window)
Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效果。
# 4.1 Sentinel控制台安装
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。
Sentinel 控制台包含如下功能:
- 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
- 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
- 规则管理和推送:统一管理推送规则。
- 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。
Sentinel控制台安装可以基于jar包启动的方式安装,也可以基于docker安装,为了方便操作,我们这里采用docker安装方式:
docker run --name=sentinel-dashboard -d -p 8858:8858 -d --restart=on-failure bladex/sentinel-dashboard:1.8.0
安装好了后,我们可以直接访问 http://192.168.200.200:8858/,默认用户名和密码都是 sentinel
登录后,效果如下:
# 4.2 接入控制台
客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信,可以通过
pom.xml
引入 JAR 包:<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>x.y.z</version> </dependency>
1
2
3
4
5如果是SpringBoot工程接入Sentinel,可以直接引入如下依赖包:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.2.5.RELEASE</version> </dependency>
1
2
3
4
5
hailtaxi-gateway接入控制台:
1、引入依赖包:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
2
3
4
5
2、在核心配置文件中配置Sentinel控制台服务地址
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: 192.168.200.200:8858
2
3
4
5
6
注意:
1、这里的
spring.cloud.sentinel.transport.port
端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互,比如限流规则拉取。2、配置信息现在是存储到nacos中,故要在nacos中找到
hailtaxi-gateway.yaml
,进行如下配置
修改之后记得发布!
3、启动各个服务,
此时我们出发一些请求操作,再看Sentinel控制台会多一个服务监控:
# 4.3 可视化管理
# 4.3.1 实时监控
同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在"实时监控"下。
注意: 实时监控仅存储 5 分钟以内的数据,如果需要持久化,需要通过调用实时监控接口 (opens new window)来定制。
如果要获取监控数据,直接调用 http://localhost:8719/clusterNode 即可获取,效果如下:
# 4.3.2 流控规则
我们可以在【流控规则】页面中新增,点击【流控规则】进入页面新增页面,如下图:
资源名:其实可以和请求路径保持一致,这里的流控模式为QPS,触发流控执行阈值为1,流控模式为让当前请求的资源快速直白。
这里的参数和我们程序中的参数其实是一样的,如下说明:
resource:资源名,即限流规则的作用对象 count: 限流阈值 grade: 限流阈值类型(QPS 或并发线程数) limitApp: 流控针对的调用来源,若为 default 则不区分调用来源 strategy: 调用关系限流策略:直接,关联,链路 controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
1
2
3
4
5
6
我们测试效果如下:
# 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>
2
3
4
5
创建过滤器:
/***
* 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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bootstrap.yml配置:web-context-unify: false
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8858
web-context-unify: false #可根据不同的URL 进行链路限流
2
3
4
5
6