文章目录
- 前言
- 第一节 Gateway网关限流
- 第二节 路由维度的限流
- 第三节 自定义API分组维度限流
前言
什么是网关限流?
限流有哪些维度?
如何自定义限流规则?
第一节 Gateway网关限流
网关是所有请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多,本次采用Sentinel组件实现网关的限流。Sentinel支持对SpringCloud Gateway、 Zuul等主流网关进行限流。
从1.6.0版本后,Sentinel提供了SpringCloud Gateway的适配模块,可以提供两种资源维度的限流
- route维度:在spring配置文件中配置的路由条目,资源名为对应的routeId
- 自定义API维度:用户可以利用Sentinel提供的API来自定义一些API分组
第二节 路由维度的限流
如何进行路由维度的限流?
- 在api-gateway模块里引入依赖
<!--sentinel网关限流-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
- 编写路由维度限流代码
package com.lcz.config;
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 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.*;
/**
* 网关限流
*/
@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;
}
//初始化一个限流过滤器
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
//配置初始化的限流参数
@PostConstruct
public void doInit() {
Set<GatewayFlowRule> rules=new HashSet<>();
rules.add(new GatewayFlowRule("product_route") //product_route 路由的id
.setCount(1) //限流阈值
.setIntervalSec(1)); //统计时间窗口,单位秒
GatewayRuleManager.loadRules(rules);
}
//配置限流的异常处理器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
//自定义限流异常页面
@PostConstruct
public void initBlockHandlers(){
BlockRequestHandler blockRequestHandler=new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map=new HashMap();
map.put("code",0);
map.put("message","API网关限流");
Mono<ServerResponse> mono=ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromObject(map));
return mono;
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
代码针对的是配置文件的product_route这个路由进行的限流。
- 启动网关测试,频繁请求,发现路由product_route被限流了。而请求其它路由则未被限流。
第三节 自定义API分组维度限流
自定义API分组限流是一种更细粒度的限流规则。
如何实现API分组限流?
- 只要对上面GatewayConfiguration进行稍作修改
package com.lcz.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 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.*;
/**
* 网关限流
*/
@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;
}
//初始化一个限流过滤器
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
//配置初始化的限流参数
@PostConstruct
public void doInit() {
Set<GatewayFlowRule> rules=new HashSet<>();
/**
* 针对路由的限流,资源名product_route为路由id
*/
rules.add(new GatewayFlowRule("product_route") //product_route 路由的id
.setCount(1) //限流阈值
.setIntervalSec(1)); //统计时间窗口,单位秒
/**
* 针对api分组的限流,设置了两组限流规则
*/
rules.add(new GatewayFlowRule("order_api1").setCount(1).setIntervalSec(1));
rules.add(new GatewayFlowRule("order_api2").setCount(1).setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
}
//配置限流的异常处理器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
//自定义限流异常页面
@PostConstruct
public void initBlockHandlers(){
BlockRequestHandler blockRequestHandler=new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map=new HashMap();
map.put("code",0);
map.put("message","API网关限流");
Mono<ServerResponse> mono=ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromObject(map));
return mono;
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
//自定义API分组
@PostConstruct
public void initCustomizedApis(){
Set<ApiDefinition> definitions=new HashSet<>();
ApiDefinition api1=new ApiDefinition("order_api1");
api1.setPredicateItems(new HashSet<ApiPredicateItem>(){
{
//可以设置多个匹配规则
add(new ApiPathPredicateItem().setPattern("/server-order/api1/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
add(new ApiPathPredicateItem().setPattern("/server-order/api3/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}
});
ApiDefinition api2=new ApiDefinition("order_api2");
api2.setPredicateItems(new HashSet<ApiPredicateItem>(){
{
//
/**
* setMatchStrategy设置匹配策略,如果不设置匹配策略,默认就是SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT,精确匹配,URL链接需相同<br>
*/
add(new ApiPathPredicateItem().setPattern("/server-order/api2/demo1"));
// add(new ApiPathPredicateItem().setPattern("/server-order/api2/demo1").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));
}
});
definitions.add(api1);
definitions.add(api2);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
}
解释一下新增的内容,新增了两组资源的限流规则,资源名为order_api1和order_api2
- 上面我们定义了对/server-order/api1/**和/server-order/api2/demo1的不同限流分组,因为调用的是order这个微服务,所以需要在order模块里面编写测试的demo
@RestController
@Slf4j
public class ApiDemoController {
@RequestMapping("/api1/demo1")
public String demo1() {
return "hello";
}
@RequestMapping("/api1/demo2")
public String demo2() {
return "hello";
}
@RequestMapping("/api2/demo1")
public String demo3() {
return "hello";
}
@RequestMapping("/api2/demo2")
public String demo4() {
return "hello";
}
}
- 启动网关和order模块测试,频繁请求http://127.0.0.1:7000/server-order/api1/demo2,提示被限流。而请求http://127.0.0.1:7000/server-order/api2/demo2 则未被限流,但是请求http://127.0.0.1:7000/server-order/api2/demo1则被限流,因为规则里添加了它。