目录
1.SpringCloudAlibabaSentinel实现网关动态限流
1.概念和来历
2.概览及控制台搭建
3.控制台有哪些能力
4.功能及设计理念
5.限流的几种方法
2.SpringCloud Alibaba Sentinel 的降级功能
1.yml中添加配置
2.编写配置类
3.编写兜底工具类
3.Sentinel还对Feigin实现了适配,支持Fenig的容错降级
3.1启动类添加注解
3.2编写feign接口
3.3编写Sentinel 对 OpenFeign 接口的降级策略
4.Sentinel结合Nacos实现限流规则持久化
4.1sentinel存储在nacos中限流数据结构
4.2添加依赖
4.3在限流资源添加限流规则
4.4在yml文件中添加配置
5.GeteWay集成Sentinel实现网关限流
5.1最简单、也是常用的测试方式:在网关内硬编码实现对请求的限流
5.2配置文件的方式编写限流规则
5.3Gateway集成Nacos+Sentiel实现网关动态限流
6.为什么需要限流
1.SpringCloudAlibabaSentinel实现网关动态限流
1.概念和来历
Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。
Sentinel的来历
1.2012年,Sentinel 诞生,主要功能为入口流量控制
2.2013-2017 年,Sentinel 在阿里巴巴集团内部迅速发展,成为基础技术模块,覆盖了所有的核心场景
3.2018 年,Sentinel 开源,并持续演进
SpringCloud Alibaba Sentinel 的基本概念
资源:可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码
方法签名 URL 服务名
规则:围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则:且所有规则可以动态实时调整
2.概览及控制台搭建
Sentinel提供一个轻量级的开源控制台(单机和集群),规则管理和推送的功能
获取并启动 Sentinel Dashboard (控制台
1.下载控制台 Jar 包: https://github.com/alibaba/Sentinel/releases
# 创建命令
mkdir -p /usr/local/sentinel
cd /usr/local/sentinel
#下载1.8.0版本
wget https://github.com/alibaba/Sentinel/releases/download/1.8.1/sentinel-dashboard-1.8.1.jar
2.nohup java -Dserver.port=7777 -Dcsp.sentinel.dashboard.server=localhost:7777 -Dproject.name=butool-sentinel-dashboard -jar /usr/local/sentinel/sentinel-dashboard-1.8.1.jar &
3.从Sentinel 1.6.0 起,Sentinel Dashboard 引入了基本的登录功能,默认的用户名密码都是 sentinel
4.打开防火墙端口
firewall-cmd --zone=public --add-port=7777/tcp --permanent
firewall-cmd --reload
4.访问地址
ip:7777
3.控制台有哪些能力
1.查看机器列表以及健康情况: 收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线
2.监控(单机和集群聚合): 通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控
3.规则管理和推送:统一管理推送规则
4.功能及设计理念
4.1流量控制
流量控制在网络传输中是一个常用的概念,
它用于调整网络包的发送数据。
然而,从系统稳定性角度考虑,
在处理请求的速度上,也有非常多的讲究。
任意时间到来的请求往往是随机不可控的,
而系统的处理能力是有限的。
我们需要根据系统的处理能力对流量进行控制。
Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,
如下图所示:
4.2流量控制设计理念
流量控制有以下几个角度:
1.资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
2.运行指标,例如 QPS、线程池、系统负载等;
3.控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
5.限流的几种方法
1.硬编码方式
1.yml文件里添加sentinel服务端地址
2.在资源方法上添加注解@SentinelResource(value="方法名")
3.配置初始化方法指定资源为SentinelResource.value
4.设置一秒请求几次
5.完成请求
6.控制台可以看到流控资源列表,看不到可能懒加载就请求一次
2.设置自定义返回信息
1.在controller中添加方法,返回值和Request方法一致,入参为BlockException
2.获取错误信息BlockException.getRuleLimitApp()
3.@SentinelResource注解中添加属性flowHandle值为方法名称
4.连续请求两次会看到结果
3.自定义通用的限流逻辑
1.创建类类中需要写静态方法,sentinel不会对对象进行new操作
2.方法返回值和Request方法一致,入参为BlockException
3.获取错误信息BlockException.getRuleLimitApp()
4.@SentinelResource注解中添加属性flowHandleClass值为类名,@SentinelResource.bolckHandler值为静态方法名称
4.基于sentinel控制台配置流控规则,根据资源名称
1.sentinel懒加载需要请求一次就会在控制台上看到资源
2.定义controller方法在控制台新增流控规则根据资源名称
@SentinelResource(
value ="byResource",blockHandler ="qinyiHandleBlockException"blockHandlerClass = ButoolBlockHandler.class
public CommonResponse<String> byResource() {}
请求一次后在控制台配置限流规则
5.在“簇点链路”中给 url 添加流控规则
在“簇点链路”中给 url 添加流控规则
@SentinelResource(value = "byUrl")public CommonResponse<String> byUrl{}
在控制台簇点链路中新增流控规则
2.SpringCloud Alibaba Sentinel 的降级功能
1.Sentinel 支持对 RestTemplate 服务调用进行保护,实现流控降级和异常降级
2.在构造RestTempplate这bean上添加@SentinelRestTemplate注解
1.yml中添加配置
# 开启或关闭 @SentinelRestTemplate 注解
resttemplate:
sentinel:
enabled: true
2.编写配置类
package cn.butool.conf;
import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* <h1>开启服务间的调用保护, 需要给 RestTemplate 做一些包装</h1>
* */
@Slf4j
@Configuration
public class SentinelConfig {
/**
* <h2>包装 RestTemplate</h2>
* */
@Bean
// @SentinelRestTemplate(
// fallback = "handleFallback", fallbackClass = RestTemplateExceptionUtil.class,
// blockHandler = "handleBlock", blockHandlerClass = RestTemplateExceptionUtil.class
// )
public RestTemplate restTemplate() {
return new RestTemplate(); // 可以对其做一些业务相关的配置
}
}
3.编写兜底工具类
package cn.butool.conf;
import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.imooc.ecommerce.vo.JwtToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
/**
* <h1>RestTemplate 在限流或异常时的兜底方法</h1>
* */
@Slf4j
public class RestTemplateExceptionUtil {
/**
* <h2>限流后的处理方法</h2>
* */
public static SentinelClientHttpResponse handleBlock(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution,
BlockException ex) {
log.error("Handle RestTemplate Block Exception: [{}], [{}]",
request.getURI().getPath(), ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse(
JSON.toJSONString(new JwtToken("butool-block"))
);
}
/**
* <h2>异常降级之后的处理方法</h2>
* */
public static SentinelClientHttpResponse handleFallback(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution,
BlockException ex) {
log.error("Handle RestTemplate Fallback Exception: [{}], [{}]",
request.getURI().getPath(), ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse(
JSON.toJSONString(new JwtToken("butool-block"))
);
}
}
3.Sentinel还对Feigin实现了适配,支持Fenig的容错降级
## 打开 Sentinel 对 Feign 的支持
feign:
sentinel:
enabled: true
3.1启动类添加注解
@EnableFeignClients
3.2编写feign接口
/**
* <h1>通过 Sentinel 对 OpenFeign 实现熔断降级</h1>
* */
@FeignClient(
value = "e-commerce-butool",
fallback = SentinelFeignClientFallback.class
)
public interface SentinelFeignClient {
@RequestMapping(value = "busl", method = RequestMethod.GET)
CommonResponse<String> getResultByFeign(@RequestParam Integer code);
}
3.3编写Sentinel 对 OpenFeign 接口的降级策略
import cn.butool.vo.CommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* <h1>Sentinel 对 OpenFeign 接口的降级策略</h1>
* */
@Slf4j
@Component
public class SentinelFeignClientFallback implements SentinelFeignClient {
@Override
public CommonResponse<String> getResultByFeign(Integer code) {
log.error("request supply for test has some error: [{}]", code);
return new CommonResponse<>(
-1,
"sentinel feign fallback",
"input code: "+ code
);
}
}
4.Sentinel结合Nacos实现限流规则持久化
1.Sentinel Dashboard 将规则保存在内存中,重启之后就会丢失,所以,考虑使用外部持久化方案
2.在Nacos 中创建规则,Nacos 会推送到客户端,Sentinel Dashboard 也会从 Nacos 去获取配置信息(远程配置中心(e.g. Nacos, ZooKeeper))
4.1sentinel存储在nacos中限流数据结构
[
{
"resource": "byResource", # 资源名
"limitApp": "default", # 流控针对的调用来源
"grade": 1, # 限流的阈值类型 1 QPS ,0 并发线程数
"count": 3, # 每1秒中能通过几个请求
"strategy": 0, # 0 直接限流 1 关联 2 链路
"controlBehavior": 0, # 控制效果:0 快速失败 1 warm Up 2 排队等待
"clusterMode": false # 是否集群效果
}
]
4.2添加依赖
<!-- Sentinel 使用 Nacos 存储规则 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
4.3在限流资源添加限流规则
/**
* <h2>在 dashboard 中 "流控规则" 中按照资源名称新增流控规则</h2>
* */
@GetMapping("/by-resource")
@SentinelResource(
value = "byResource",
blockHandler = "ButoolHandleBlockException",
blockHandlerClass = ButoolBlockHandler.class
)
public CommonResponse<String> byResource() {
log.info("coming in rate limit controller by resource");
return new CommonResponse<>(0, "", "byResource");
}
package cn.butool.block_handler;
import cn.butool.vo.CommonResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
/**
* <h1>自定义通用的限流处理逻辑</h1>
* */
@Slf4j
public class ButoolBlockHandler {
/**
* <h2>通用限流处理方法</h2>
* 这个方法必须是 static 的
* */
public static CommonResponse<String> ButoolHandleBlockException(BlockException exception) {
log.error("trigger qinyi block handler: [{}], [{}]",
JSON.toJSONString(exception.getRule()), exception.getRuleLimitApp());
return new CommonResponse<>(
-1,
"flow rule trigger block exception",
null
);
}
}
4.4在yml文件中添加配置
datasource:
# 名称任意, 代表数据源
ds:
nacos:
# NacosDataSourceProperties.java 中定义
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-sentinel
namespace: ${spring.cloud.nacos.discovery.namespace}
groupId: DEFAULT_GROUP
data-type: json
# 规则类型: com.alibaba.cloud.sentinel.datasource.RuleType
# FlowRule 就是限流规则
rule-type: flow
# 服务启动直接建立心跳连接
eager: true
5.GeteWay集成Sentinel实现网关限流
5.1最简单、也是常用的测试方式:在网关内硬编码实现对请求的限流
[
{
"resource": "nacos-client-api",
"resourceMode": 1, # 规则与resource相绑定
"count": 1, # 限流阈值
"Grade": 1, # 限流的阈值类型 1 QPS ,0 并发线程数
"intervalSec": 60 # 统计范围 60秒内允许1个请求
}
]
1网关服务添加依赖
<!-- 集成 Sentinel, 在网关层面实现限流 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- Sentinel 使用 Nacos 存储规则 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2yml中添加配置
sentinel: # 与nacos属性平齐
# 配置 sentinel dashboard 地址
transport:
dashboard: 192.168.1.11:7777
clientIp: 192.168.1.7
port: 8719 # 会在应用对应的机器上启动一个 Http Server, 该 Server 会与 Sentinel 控制台做交互
3sentinel服务端启动命令修改
# 支持网关
nohup java -Dcsp.sentinel.app.type=1 -Dserver.port=7777 -Dcsp.sentinel.dashboard.server=localhost:7777 -Dproject.name=butool-sentinel-gateway -jar /usr/local/sentinel/sentinel-dashboard-1.8.1.jar &
4网关集成配置类
package cn.butool.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.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import lombok.extern.slf4j.Slf4j;
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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.*;
/**
* <h1>Gateway 集成 Sentinel 实现限流</h1>
* */
@Slf4j
@Configuration
public class SentinelGatewayConfiguration {
/** 视图解析器 */
private final List<ViewResolver> viewResolvers;
/** HTTP 请求和响应数据的编解码配置 */
private final ServerCodecConfigurer serverCodecConfigurer;
/**
* <h2>构造方法</h2>
* */
public SentinelGatewayConfiguration(
ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer
) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* <h2>限流异常处理器, 限流异常出现时, 执行到这个 handler</h2>
* */
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// 默认会返回错误 message, code 429
return new SentinelGatewayBlockExceptionHandler(
this.viewResolvers,
this.serverCodecConfigurer
);
}
/**
* <h2>限流过滤器, 是 Gateway 全局过滤器, 优先级定义为最高</h2>
* */
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
/**
*
* 初始化限流规则
* */
@PostConstruct
public void doInit(){
log.info("--------------------------------------");
log.info("load sentinel gateway rules (code define)");
initGatewayRules();
log.info("--------------------------------------");
}
/**
* 硬编码网关限流规则
*/
private void initGatewayRules(){
Set<GatewayFlowRule> rules = new HashSet<>();
GatewayFlowRule rule = new GatewayFlowRule();
// 指定限流模式, 根据 route_id 做限流, 默认的模式
rule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID);
// 指定 route_id -> service id
rule.setResource("e-commerce-nacos-client");
// 按照 QPS 限流
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 统计窗口和限流阈值
rule.setIntervalSec(60);
rule.setCount(3);
rules.add(rule);
// 加载到网关中
GatewayRuleManager.loadRules(rules);
}
}
4请求指定的服务查看限流情况
// 指定 route_id -> service id rule.setResource("e-commerce-nacos-client"); 60秒内请求三次以上报下面错误
5自定义返回错误信息
/**
*
* 初始化限流规则
* */
@PostConstruct
public void doInit(){
log.info("--------------------------------------");
log.info("load sentinel gateway rules (code define)");
initGatewayRules();
log.info("--------------------------------------");
// 加载自定义限流异常处理器
initBlockHandler();
}
/**
* <h2>自定义限流异常处理器</h2>
* */
private void initBlockHandler() {
// 自定义 BlockRequestHandler
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange,
Throwable throwable) {
log.error("------------- trigger gateway sentinel rule -------------");
Map<String, String> result = new HashMap<>();
result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
result.put("message", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
result.put("route", "e-commerce-nacos-client");
return ServerResponse
.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(result));
}
};
// 设置自定义限流异常处理器
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
6.自定义分组限流
/**
* 硬编码网关限流规则
*/
private void initGatewayRules(){
Set<GatewayFlowRule> rules = new HashSet<>();
// 限流分组, Sentinel 先去找规则定义, 再去找规则中定义的分组
rules.add(
new GatewayFlowRule("nacos-client-api-1")
.setCount(3).setIntervalSec(60)
);
rules.add(
new GatewayFlowRule("nacos-client-api-2")
.setCount(1).setIntervalSec(60)
);
// 加载到网关中
GatewayRuleManager.loadRules(rules);
// 加载限流分组
initCustomizedApis();
}
/**
* <h2>硬编码网关限流分组</h2>
* 1. 最大限制 - 演示
* 2. 具体的分组
* */
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
// nacos-client-api 组, 最大的限制
ApiDefinition api = new ApiDefinition("nacos-client-api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
// 模糊匹配 /butool/ecommerce-nacos-client/ 及其子路径的所有请求
add(new ApiPathPredicateItem()
.setPattern("/butool/ecommerce-nacos-client/**")
// 根据前缀匹配
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
// nacos-client-api-1 分组
ApiDefinition api1 = new ApiDefinition("nacos-client-api-1")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem()
// 精确匹配 /butool/ecommerce-nacos-client/nacos-client/service-instance
.setPattern("/butool/ecommerce-nacos-client/nacos-client/service-instance"));
}});
// nacos-client-api-2 分组
ApiDefinition api2 = new ApiDefinition("nacos-client-api-2")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem()
// 精确匹配 /butool/ecommerce-nacos-client/nacos-client/project-config
.setPattern("/butool/ecommerce-nacos-client" +
"/nacos-client/project-config"));
}});
definitions.add(api1);
definitions.add(api2);
// 加载限流分组
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
5.2配置文件的方式编写限流规则
1.yml文件
硬编码工作较多,相对来说比较繁琐;熟悉之后可以进一步使用配置文件的形式
1.resources目录下存放文件
2.yml文件中配置
sentinel:
# 配置 sentinel dashboard 地址
transport:
dashboard: 192.168.1.11:7777
clientIp: 192.168.1.7
port: 8719 # 会在应用对应的机器上启动一个 Http Server, 该 Server 会与 Sentinel 控制台做交互
datasource:
#通过本地文件方式, 基于服务级别的配置
dsl.file:
file: classpath:gateway-flow-rule-sentinel.json
# 代表服务级别的限流, 一步步点进去看, 文件类型
ruleType: gw-flow
# 通过本地文件方式, 细粒度对指定 api 进行配置
ds2.file:
file: classpath:gateway-flow-rule-api-sentinel.json
# 代表 API 分组, 一步步点进去看, 文件类型
ruleType: gw-api-group
2.json文件
## 文件名 gateway-flow-rule-api-sentinel.json
[
{
"apiName": "nacos-client-api",
"predicateItems": [
{
"pattern": "/butool/ecommerce-nacos-client/nacos-client/project-config"
},
{
"pattern": "/butool/ecommerce-nacos-client/**",
"matchStrategy": 1
}
]
}
]
## 文件名 gateway-flow-rule-sentinel.json
[
{
"resource": "e-commerce-nacos-client",
"resourceMode": 0,
"count": 3,
"intervalSec": 60
},
{
"resource": "nacos-client-api",
"resourceMode": 1,
"count": 1,
"intervalSec": 60
}
]
5.配置类
package cn.butool.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.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import lombok.extern.slf4j.Slf4j;
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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.*;
/**
* <h1>Gateway 集成 Sentinel 实现限流</h1>
* */
@Slf4j
@Configuration
public class SentinelGatewayConfiguration {
/** 视图解析器 */
private final List<ViewResolver> viewResolvers;
/** HTTP 请求和响应数据的编解码配置 */
private final ServerCodecConfigurer serverCodecConfigurer;
/**
* <h2>构造方法</h2>
* */
public SentinelGatewayConfiguration(
ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer
) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* <h2>限流异常处理器, 限流异常出现时, 执行到这个 handler</h2>
* */
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// 默认会返回错误 message, code 429
return new SentinelGatewayBlockExceptionHandler(
this.viewResolvers,
this.serverCodecConfigurer
);
}
/**
* <h2>限流过滤器, 是 Gateway 全局过滤器, 优先级定义为最高</h2>
* */
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
}
5.3Gateway集成Nacos+Sentiel实现网关动态限流
1.nacos中添加配置
2.json内容
## dataId gateway-flow-rule-api-sentinel
[
{
"apiName": "nacos-client-api",
"predicateItems": [
{
"pattern": "/butool/ecommerce-nacos-client/nacos-client/project-config"
},
{
"pattern": "/butool/ecommerce-nacos-client/**",
"matchStrategy": 1
}
]
}
]
## dataId gateway-flow-rule-sentinel
[
{
"resource": "e-commerce-nacos-client",
"resourceMode": 0,
"count": 3,
"intervalSec": 60
},
{
"resource": "nacos-client-api",
"resourceMode": 1,
"count": 1,
"intervalSec": 60
}
]
6.为什么需要限流
1.平台用户增长过快 2.热点事件、热点接口、热点 Key 3.恶意请求
1.实现原理
Sentinel 通过实现 Filter,对路由/API分组匹配、请求属性解析和参数组装实现限流