谷粒商城-高级篇-Sentinel-分布式系统的流量防卫兵
1、基本概念
1.1、熔断降级限流
1、什么是熔断
A 服务调用 B 服务的某个功能,由于网络不稳定问题,或者 B 服务卡机,导致功能时间超长。如果这样子的次数太多。我们就可以直接将 B 断路了( A 不再请求 B 接口),凡是调用 B 的直接返回降级数据,不必等待 B 的超长执行。 这样 B 的故障问题,就不会级联影响到 A 。
2、什么是降级
整个网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级[ 停止服务,所有的调用直接返回降级数据 ] 。以此缓解服务器资源的的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户的得到正确的相应。
异同:
相同点:
为了保证集群大部分服务的可用性和可靠性,防止崩溃,牺牲小我
用户最终都是体验到某个功能不可用
不同点:
熔断是被调用方故障,触发的系统主动规则
降级是基于全局考虑,停止一些正常服务,释放资源
3、什么是限流
对打入服务的请求流量进行控制,使服务能够承担不超过自己能力的流量压力
1.2、Sentinel 简介
官方文档: 介绍 · alibaba/Sentinel Wiki · GitHub
项目地址: GitHub - alibaba/Sentinel: A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 分为两个部分 :
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的Tomcat 等应用容器。
Sentinel 基本概念
资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来 。大部分情况下,可以使用方法签名,URL ,甚至服务名称作为资源名来标示资源。
规则
围绕资源的实时状态设定的规则,可以包括 流量控制规则 、 熔断降级规则 以及 系统保护规 则 。所有规则可以动态实时调整。
1.3、Hystrix 与 Sentinel 比较
2、整合SpringBoot
1、整合sentinel流程:
1)、导入依赖 spring-cloud-starter-alibaba-sentinel
2)、下载Sentinel的控制台
3)、配置sentinel控制台地址信息
4)、在控制台调整参数【默认所有的流控设置保存在项目内存中,重启失效】
2、每一个微服务导入信息审计模块spring-boot-starter-actuator
并配置management.endpoints.web.exposure.include=* (暴露Sentinel的信息)
3、自定义Sentinel的流控返回数据
gulimall-common模块的pom.xml文件添加sentinel熔断、限流依赖
<!--sentinel熔断、限流-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
gulimall-seckill模块的pom.xml文件添加统计审计信息依赖
<!--统计审计信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
找到项目里sentinel对应的版本
去官网下载该版本的控制台
Tags · alibaba/Sentinel · GitHub
java -jar sentinel-dashboard-1.8.6.jar --server.port=8333
打开http://localhost:8333
用户名:sentinel
密码:sentinel
在每个服务配置里配置Sentinel
#Sentinel
#Sentinel控制台地址
spring.cloud.sentinel.transport.dashboard=localhost:8333
#Sentinel传输端口:默认 8719,假如被占用了会自动从 8719 开始依次 +1 扫描,直至找到未被占用的端口
spring.cloud.sentinel.transport.port=8719
#暴露的 endpoint 路径为 /actuator/sentinel
#Sentinel Endpoint 里暴露的信息非常有用。包括当前应用的所有规则信息、日志目录、
#当前实例的 IP,Sentinel Dashboard 地址,Block Page,应用与 Sentinel Dashboard 的心跳频率等等信息。
management.endpoints.web.exposure.include=*
未使用自定义流控响应
流控生效(多次刷新,只调用少数)
http://seckill.gulimall.com/getCurrentSeckillSkus
3、自定义流控响应
添加“com.atguigu.gulimall.seckill.config.SeckillSentinelConfig”类,代码如下:
package com.atguigu.gulimall.seckill.config;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.atguigu.common.exception.BizCodeEnume;
import com.atguigu.common.utils.R;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 自定义sentinel流控响应
@Configuration
public class GulimallSeckillSentinelConfig {
public GulimallSeckillSentinelConfig() {
// 自定义返回字符串
//{"msg":"请求次数太多,请稍后再试","code":10003}
WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {
R error = R.error(BizCodeEnume.TOO_MANY_REQUESTS_EXCEPTION.getCode(), BizCodeEnume.TOO_MANY_REQUESTS_EXCEPTION.getMsg());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(JSON.toJSONString(error));
}
});
}
}
@Configuration
public class SeckillSentinelConfig implements BlockExceptionHandler {
/**
* 2.2.0以后的版本实现的是BlockExceptionHandler;以前的版本实现的是WebCallbackManager
* @param httpServletRequest
* @param httpServletResponse
* @param e
* @throws Exception
*/
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
R error = R.error(BizCodeEnume.TOO_MANY_REQUESTS_EXCEPTION.getCode(), BizCodeEnume.TOO_MANY_REQUESTS_EXCEPTION.getMsg());
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json");
httpServletResponse.getWriter().write(JSON.toJSONString(error));
}
/**
* 因为版本冲突导致无法引入 WebCallbackManager
*/
// public SeckillSentinelConfig() {
// WebCallbackManager.setUrlBlockHandler((request, response, ex) -> {
// R error = R.error(BizCodeEnum.TOO_MANY_REQUESTS_EXCEPTION.getCode(), BizCodeEnum.TOO_MANY_REQUESTS_EXCEPTION.getMsg());
// response.setCharacterEncoding("UTF-8");
// response.setContentType("application/json");
// response.getWriter().write(JSON.toJSONString(error));
// });
// }
}
返回结果
4、Sentinel全服务引入
每个微服务引入actuator依赖
<!--统计审计信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
每个配置文件配置以下参数
#Sentinel
#Sentinel控制台地址
spring.cloud.sentinel.transport.dashboard=localhost:8333
#Sentinel传输端口:默认 8719,假如被占用了会自动从 8719 开始依次 +1 扫描,直至找到未被占用的端口
spring.cloud.sentinel.transport.port=8719
#暴露的 endpoint 路径为 /actuator/sentinel
#Sentinel Endpoint 里暴露的信息非常有用。包括当前应用的所有规则信息、日志目录、
#当前实例的 IP,Sentinel Dashboard 地址,Block Page,应用与 Sentinel Dashboard 的心跳频率等等信息。
management.endpoints.web.exposure.include=*
流量控制
设置超时时间等参数。
5、feign熔断降级
使用Sentinel来保护Feign的远程调用。
主页 · alibaba/Sentinel Wiki · GitHub
什么是熔断降级
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
注意:本文档针对 Sentinel 1.8.0 及以上版本。1.8.0 版本对熔断降级特性进行了全新的改进升级,请使用最新版本以更好地利用熔断降级的能力。
熔断降级设计理念
在限制的手段上, Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过线程池隔离的方式,来对依赖(在 Sentinel 的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。
Sentinel 对这个问题采取了两种手段 :
通过并发线程数进行限制
和资源池隔离的方法不同, Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其
它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个
资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步
堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积
的线程完成任务后才开始继续接收请求。
通过响应时间对资源进行降级
除了对并发线程数进行控制以外, Sentinel 还可以通过响应时间来快速降级不稳定的资源。
当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的
时间窗口之后才重新恢复。
熔断策略
Sentinel 提供以下几种熔断策略:
1.慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
2.异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
3.异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
整合测试:
https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/se%20ntinel-example/sentinel-feign-example/readme-zh.md
默认情况下,sentinel是不会对feign进行监控的,需要开启配置
在gulimall-product类配置文件添加配置
#sentinel是不会对feign进行监控的,需要开启配置
feign.sentinel.enabled=true
5.1、fegin的熔断
使用Sentinel来保护feign远程调用:熔断。
1.调用方的熔断保护开启 feign.sentinel.enabled=true
2.调用方手动指定远程服务的降级策略。远程服务被降级处理,触发我们的熔断回调方法
3.超大浏览的时候,必须牺牲一些远程服务。在服务的提供方(远程服务==>秒杀)指定降级策略; 提供方是在运行。但是不运行自己的业务逻辑。返回的是默认的降级数据(限流的数据)===一般在提供方降级,考虑全局的性能。
gulimall-product服务远程调用gulimall-seckill服务。调用前直接把gulimall-seckill服务宕机,以前不使用sentinel的熔断的时候直接调用会报错。
现在使用sentinel的熔断保护机制,远程调用失败,我们会在调用方实现远程调用类,自定义返回信息给页面。
后台配置熔断策略
定义FeignClient及其降级配置
修改“com.atguigu.gulimall.product.feign.SeckillFeignService”类,代码如下:
@FeignClient(value = "gulimall-seckill",fallback = SeckillFeignServiceFallBack.class)
public interface SeckillFeignService {
@GetMapping("/sku/seckill/{skuId}")
R getSkuSecKillInfo(@PathVariable("skuId") Long skuId);
}
自定义--远程调用失败具体的fallback 实现
添加“com.atguigu.gulimall.product.feign.fallback.SeckillFeignServiceFallBack”类,代码如下:
@Slf4j
@Component
public class SeckillFeignServiceFallBack implements SeckillFeignService {
@Override
public R getSkuSecKillInfo(Long skuId) {
log.info("熔断方法调用.....getSkuSecKillInfo");
return R.error(BizCodeEnume.TOO_MANY_REQUESTS_EXCEPTION.getCode(),BizCodeEnume.TOO_MANY_REQUESTS_EXCEPTION.getMsg());
}
}
6、自定义受保护资源
spring-cloud-alibaba/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md at 2.2.x · alibaba/spring-cloud-alibaba · GitHub 注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。示例:
Entry entry = null;
try {
entry = SphU.entry(key, EntryType.IN, key);
// Write your biz code here.
// <<BIZ CODE>>
} catch (Throwable t) {
if (!BlockException.isBlockException(t)) {
Tracer.trace(t);
}
} finally {
if (entry != null) {
entry.exit();
}
}
开源整合模块,如 Sentinel Dubbo Adapter, Sentinel Web Servlet Filter 或 @SentinelResource 注解会自动统计业务异常,无需手动调用。
熔断降级规则说明
熔断降级规则(DegradeRule)包含下面几个重要的属性:
熔断器事件监听
Sentinel 支持注册自定义的事件监听器监听熔断器状态变换事件(state change event)。示例:
EventObserverRegistry.getInstance().addStateChangeObserver("logging",
(prevState, newState, rule, snapshotValue) -> {
if (newState == State.OPEN) {
// 变换至 OPEN state 时会携带触发时的值
System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(),
TimeUtil.currentTimeMillis(), snapshotValue));
} else {
System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(),
TimeUtil.currentTimeMillis()));
}
});
自定义受保护的资源方法:
1)、代码:
try(Entry entry = SphU.entry("seckillSkus")){
// 业务逻辑
}catch(Exception e){
}
2)、基于注解
@SentinelResource(value = "getCurrentSeckillSkusResource",blockHandler = "blockHandler")
无论1.2一定要配置被限流以后的默认返回
url请求可以设置统一返回 SeckillSentinelConfig
6.1、基于代码的限流
修改“com.atguigu.gulimall.seckill.service.impl.SeckillServiceImpl”类,代码如下:
@Override
public List<SeckillSkuRedisTo> getCurrentSeckillSkus() {
try (Entry entry = SphU.entry("seckillSkus")) {
// 获取秒杀活动的所有key
Set<String> keys = stringRedisTemplate.keys(SESSIONS_CACHE_PREFIX + "*");
long currentTime = System.currentTimeMillis();
for (String key : keys) {
String replace = key.replace(SESSIONS_CACHE_PREFIX, "");
String[] split = replace.split("_");
long startTime = Long.parseLong(split[0]);
long endTime = Long.parseLong(split[1]);
// 当前秒杀活动处于有效期内
if (currentTime > startTime && currentTime < endTime) {
// 获取这个秒杀场次的所有商品信息
List<String> range = stringRedisTemplate.opsForList().range(key, -100, 100);
BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
assert range != null;
List<String> strings = hashOps.multiGet(range);
if (!CollectionUtils.isEmpty(strings)) {
return strings.stream().map(item -> JSON.parseObject(item, SeckillSkuRedisTo.class))
.collect(Collectors.toList());
}
break;
}
}
} catch (BlockException e) {
log.error("资源被限流{}", e.getMessage());
}
return null;
}
6.2、基于注解的自定义限流
@SentinelResource(value = "getCurrentSeckillSkusResource", blockHandler = "blockHandler")
无论是1,2方式一定要配置被限流以后的默认返回
url请求可以设置统-返回:WebCallbackManager(在config里)
public List<SeckillSkuRedisTo> blockHandler(BlockException e){
log.error("getCurrentSeckillSkusResource被限流了。。。。");
return null;
}
// blockHandler 函数会在原方法被限流/降级/系统保护的时候调用,而fallback函数会针对所有类型的异常
//基于注解的限流
@SentinelResource(value = "getCurrentSeckillSkusResource", blockHandler = "blockHandler")
@Override
public List<SeckillSkuRedisTo> getCurrentSeckillSkus() {
try (Entry entry = SphU.entry("seckillSkus")) {
// 获取秒杀活动的所有key
Set<String> keys = stringRedisTemplate.keys(SESSIONS_CACHE_PREFIX + "*");
long currentTime = System.currentTimeMillis();
for (String key : keys) {
String replace = key.replace(SESSIONS_CACHE_PREFIX, "");
String[] split = replace.split("_");
long startTime = Long.parseLong(split[0]);
long endTime = Long.parseLong(split[1]);
// 当前秒杀活动处于有效期内
if (currentTime > startTime && currentTime < endTime) {
// 获取这个秒杀场次的所有商品信息
List<String> range = stringRedisTemplate.opsForList().range(key, -100, 100);
BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
assert range != null;
List<String> strings = hashOps.multiGet(range);
if (!CollectionUtils.isEmpty(strings)) {
return strings.stream().map(item -> JSON.parseObject(item, SeckillSkuRedisTo.class))
.collect(Collectors.toList());
}
break;
}
}
} catch (BlockException e) {
log.error("资源被限流{}", e.getMessage());
}
return null;
}
7、网关流控
如果能在网关层就进行流控,可以避免请求流入业务,减小服务压力
Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。
Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:
1.GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
2.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) 注册动态规则源动态推送(推荐方式)。
Spring Cloud Gateway
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
使用时需引入以下模块(以 Maven 为例)
gulimall-gateway引入依赖
<!-- 引入sentinel网关限流 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
注意引入的依赖要和gulimall-common的pom里的阿里巴巴版本一致
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
参数解释:
间隔就是:当每秒的请求超过QPS值,启动间隔。2分钟内所有调用这个接口直接返回。不去调用业务代码
自定义返回数据信息
您可以在 GatewayCallbackManager 注册回调进行定制:
setBlockHandler:注册函数用于实现自定义的逻辑处理被限流的请求,对应接口为 BlockRequestHandler。默认实现为 DefaultBlockRequestHandler,当被限流时会返回类似于下面的错误信息:Blocked by Sentinel: FlowException。
注意:
Sentinel 网关流控默认的粒度是 route 维度以及自定义 API 分组维度,默认不支持 URL 粒度。若通过 Spring Cloud Alibaba 接入,请将 spring.cloud.sentinel.filter.enabled 配置项置为 false(若在网关流控控制台上看到了 URL 资源,就是此配置项没有置为 false)。
若使用 Spring Cloud Alibaba Sentinel 数据源模块,需要注意网关流控规则数据源类型是 gw-flow,若将网关流控规则数据源指定为 flow 则不生效。
新增“com.atguigu.gulimall.gateway.config.SentinelGateWayConfig”类,代码如下:
SentinelGateWayConfig(自定义返回类)Mono
@Configuration
public class SentinelGateWayConfig {
// 响应式编程
public SentinelGateWayConfig() {
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
// 网关限流了请求,就会调用此回调
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
R error = R.error(BizCodeEnume.TOO_MANY_REQUESTS_EXCEPTION.getCode(), BizCodeEnume.TOO_MANY_REQUESTS_EXCEPTION.getMsg());
String s = JSON.toJSONString(error);
Mono<ServerResponse> body = ServerResponse.ok().body(Mono.just(s), String.class);
return body;
}
});
}
}