1.准备一个gateway微服务
导入以下依赖:
<!-- SpringCloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- SpringCloud Ailibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Ailibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud Ailibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringCloud Ailibaba Sentinel Gateway -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- Sentinel Datasource Nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
加入以下配置(gateway与sentinel相关配置放在nacos,此处方便查看):
# Tomcat
server:
port: 8080
# Spring
spring:
application:
# 应用名称
name: gateway
profiles:
# 环境配置
active: dev
main:
allow-bean-definition-overriding: true
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 192.168.1.0:8848
config:
# 配置中心地址
server-addr: 192.168.1.0:8848
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
sentinel:
# 取消控制台懒加载
eager: true
transport:
# sentinel控制台地址
dashboard: 192.168.1.0:8080
# nacos配置持久化
datasource:
ds1:
nacos:
# nacos地址
server-addr: 192.168.1.0:8848
dataId: sentinel-gateway
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes:
# 认证中心
- id: auth
uri: lb://auth
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
# 不校验白名单
ignore:
whites:
- /auth/logout
- /auth/login
- /*/v2/api-docs
- /csrf
其中,sentinel在nacos上的限流策略如下:
[
{
"resource": "system",
"count": 500,
"grade": 1,
"limitApp": "default",
"strategy": 0,
"controlBehavior": 0
}
]
其中网关限流规则 GatewayFlowRule 的字段解释如下:
参数 | 描述 | 描述 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
limitApp | 流控针对的调用来源,若为 default 则不区分调用来源 | default,代表不区分调用来源 |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 0 基于线程数 , 1 基于QPS |
count | 限流阈值 | |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) 0 根据调用方限流(limitApp), 1 根据调用链路入口限流, 2 具有关系的资源流量控制 |
controlBehavior | 流量控制效果(直接拒绝、Warm Up、匀速排队) | 直接拒绝 0 直接拒绝, 1 Warm Up, 2 匀速排队 |
clusterMode | 是否集群限流 | 否 |
此处得配置指1s钟内,gateway转发到服务system得qps为500。
2.配置GatewayConfig
package com.xrj.gateway.config;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.xrj.gateway.handler.SentinelFallbackHandler;
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;
/**
* 网关限流配置
*/
@Configuration
public class GatewayConfig
{
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
{
return new SentinelFallbackHandler();
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter()
{
return new SentinelGatewayFilter();
}
}
其中SentinelFallbackHandler如下:
package cn.witeye.vpais.gateway.handler;
import java.nio.charset.StandardCharsets;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;
/**
* 自定义限流异常处理
*/
public class SentinelFallbackHandler implements WebExceptionHandler
{
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
{
ServerHttpResponse serverHttpResponse = exchange.getResponse();
serverHttpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
byte[] datas = "{\"code\":500,\"msg\":\"当前请求量过大,请稍后再试\"}".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);
}
}
3.启动类
/**
* 网关启动程序
*/
@EnableDiscoveryClient
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class GatewayApplication
{
public static void main(String[] args)
{
SpringApplication.run(GatewayApplication.class, args);
}
}
4.测试
使用jmeter压测工具:
可以发现,当qps超过设置得阈值时,请求会被拦截下来,提示当前请求量过大!