Cloud Gateway网关限流实现
SpringCloudGateway配合Sentinel实现限流
前述
Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。
从1.6.0版本开始,Sentinel提供了SpringCloud Gateway的适配模块,可以提供两种资源维度的限流:
GatewayFlowRule
:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。ApiDefinition
:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫my_api
,请求 path 模式为/foo/**
和/baz/**
的都归到my_api
这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
网关限流实现
网关模块中引入依赖
<!--nacos注册中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--spring cloud gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- spring cloud gateway整合sentinel的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- sentinel的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置文件
spring:
main:
allow-bean-definition-overriding: true
application:
name: cloud-gateway
cloud:
## 整合sentinel,配置sentinel控制台的地址
sentinel:
# 取消控制台懒加载
eager: true
transport:
dashboard: localhost:8080 # 配置Sentinel dashboard 地址
port: 8719
filter:
enabled: false # 排除将网关限流
nacos:
discovery:
server-addr: localhost:8848
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
lower-case-service-id: true #是否将服务名称转小写
routes:
- id: payment-service #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/** # 断言,路径相匹配的进行路由
限流配置
经过上述两个步骤已经整合好了 Sentinel
,访问下接口 localhost:9527/payment/paymentSQL/1
然后通过 sentinel
控制台我们可以看见已经被监控到了,监控的路由是 payment-service
我们点击 流控
新增一个限流规则
上图中对 payment-service
这个路由做出了限流,QPS阈值为1。
此时快速访问:localhost:9527/payment/paymentSQL/1
可以观察到已经被限流了
自定义限流异常信息
配置文件中配置
1.1、配置文件配置限流信息
spring:
cloud:
## 整合sentinel,配置sentinel控制台的地址
sentinel:
#配置限流之后,响应内容
scg:
fallback:
## 两种模式,一种是response返回文字提示信息,
## 一种是redirect,重定向跳转,需要同时配置redirect(跳转的uri)
mode: response
## 响应的状态
response-status: 200
## 响应体
response-body: '{"code": 200,"message": "请求失败,稍后重试!"}'
1.2、重定向配置
spring:
cloud:
## 整合sentinel,配置sentinel控制台的地址
sentinel:
#配置限流之后,响应内容
scg:
fallback:
## 两种模式,一种是response返回文字提示信息,一种是redirect,重定向跳转,需要同时配置redirect(跳转的uri)
mode: redirect
## 跳转的URL
redirect: http://www.baidu.com
代码实现
/**
* @ClassName: GatewaySentinelConfig
* @Description: 网关限流配置,使用Sentinel控制台来定义限流规则
* @author: LiJunYi
*/
@Configuration
public class GatewaySentinelConfig
{
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
{
return new SentinelFallbackHandler();
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter()
{
return new SentinelGatewayFilter();
}
}
/**
* @ClassName: SentinelFallbackHandler
* @Description: 自定义限流异常处理
* @author: LiJunYi
*/
public class SentinelFallbackHandler implements WebExceptionHandler
{
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
{
ServerHttpResponse serverHttpResponse = exchange.getResponse();
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
byte[] datas = "{\"status\":429,\"message\":\"请求超过最大数,请稍后再试\"}".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
return serverHttpResponse.writeWith(Mono.just(buffer));
}
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
{
if (exchange.getResponse().isCommitted())
{
return Mono.error(ex);
}
if (!BlockException.isBlockException(ex))
{
return Mono.error(ex);
}
return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));
}
private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable)
{
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
}
}