SpringCloudGateway基于Sentinel的限流
Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。
Sentinel介绍:
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
* route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
* 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:
* GatewayFlowRule :网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同
route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的
限流。
* ApiDefinition :用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以
定义一个 API 叫 my_api ,请求 path 模式为 /foo/** 和 /baz/** 的都归到 my_api 这个 API
分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
(1)环境搭建
导入Sentinel 的响应依赖
<!-- sentinel限流依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.7.2</version>
</dependency>
(2)编写配置类
package com.kerry.gateway.filter;
@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;
}
/**
* 配置限流的异常处理器:SentinelGatewayBlockExceptionHandler
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer);
}
/**
* 配置限流过滤器
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
/**
* 配置初始化的限流参数
*/
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
// rules.add( new GatewayFlowRule("category-service") //资源名称 //普通全局限流
// .setCount(1) // 限流阈值
// .setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
// );
//http://localhost:8001/admin/category/demo.do/1?id=1 当链接带有参数id时,会匹配到限流中
//http://localhost:8001/admin/category/demo.do/1 如果没有带参数id时,不会被匹配进限流中。
// rules.add(new GatewayFlowRule("category-service") //参数限流
// .setCount(1)
// .setIntervalSec(1)
// .setParamItem(new GatewayParamFlowItem()
// .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM).setFieldName("id")
// )
// );
//上面的话是直接对资源进行限流
//下面是通过自定义api分组进行限流 其里面的资源都在下面进行设置好了对应的匹配路径.
rules.add(new GatewayFlowRule("category_api").setCount(1).setIntervalSec(1));
rules.add(new GatewayFlowRule("headline_api").setCount(1).setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
}
// 自定义API分组
@PostConstruct
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("category_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
//以/product-service/product 开头的请求
add(new ApiPathPredicateItem().setPattern("/admin/category/**").
setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
ApiDefinition api2 = new ApiDefinition("headline_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
///order-service/order 完成的url路径匹配
//add(new ApiPathPredicateItem().setPattern("/order-service/order"));
//add(new ApiPathPredicateItem().setPattern("/order/buy"));
add(new ApiPathPredicateItem().setPattern("/admin/headline/**").
setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(api1);
definitions.add(api2);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
//自定义异常提示
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map = new HashMap<>();
map.put("code", 001);
map.put("message", "对不起,接口限流了");
return ServerResponse.status(HttpStatus.OK).//状态码200
contentType(MediaType.APPLICATION_JSON_UTF8).//application/json;charset=UTF-8
body(BodyInserters.fromObject(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
(3)网关配置
server:
port: 8001
spring:
application:
name: server-gateway #服务名称
cloud:
gateway:
routes:
- id: category-service
uri: lb://service-category
predicates:
- Path=/category/**,/admin/category/**
filters: #限流功能
- RewritePath=/admin/category/(?<segment>.*), /admin/category/$\{segment}
- id: headline-service
uri: lb://service-headline
predicates:
- Path=/headline/**,/admin/headline/**
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8000/eureka
registry-fetch-interval-seconds: 5 # 获取服务列表的周期:5s
instance:
prefer-ip-address: true #使用ip注册
ip-address: 127.0.0.1