文章目录
一、什么是Spring Cloud Gateway
网关作为流量的入口,常用的功能包括路由转发,权限校验,限流等。
Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul。相比 Zuul 来说,Spring Cloud Gateway 提供更优秀的性能,更强大的有功能。
Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包。
Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。
1.1 核心概念
路由(route)
路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的URL和配置的路由匹配。
断言(predicates)
Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。
**过滤器(Filter) **
SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理。
1.2 工作原理
Spring Cloud Gateway 的工作原理跟 Zuul 的差不多,最大的区别就是 Gateway 的 Filter 只有 pre 和 post 两种。
客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。
过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求;代理请求完成后,执行 post 过滤器逻辑。
二、Spring Cloud Gateway快速开始
2.1 环境搭建
1) 引入依赖
<!-- gateway网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- nacos服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
注意:会和spring-boot-starter-web
的依赖冲突,不需要添加spring-boot-starter-web
。依赖
**********************************************************
Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.
*********************************************************
2) 编写yml配置文件
server:
port: 8888
spring:
application:
name: mall-gateway
#配置nacos注册中心地址
cloud:
nacos:
discovery:
# 添加mall专属的命名空间以进行资源隔离
namespace: mall
server-addr: 192.168.131.172:8848
gateway:
discovery:
locator:
# 默认为false,设为true开启通过微服务创建路由的功能,即可以通过微服务名访问服务
# http://localhost:8888/mall-order/order/findOrderByUserId/1
# 如果mall-order配到某个微服务了,会将http://localhost:8888/mall-order替换成微服务对于的IP地址
enabled: true
# 是否开启网关
enabled: true
spring.cloud.gateway.discovery.locator.enabled属性默认为false,官网也不推荐我们设置为false。因为从安全性角度考虑,微服务是内部服务,不应该将其名称暴露给前端。
那么不暴露微服务名称,该怎么确定微服务具体的地址呢?这也是GateWay需要解决的问题。会用断言工厂的方式解决。
3)测试
2.2 路由断言工厂(Route Predicate Factories)配置
网关启动日志:
2.2.1 路径匹配(推荐使用lb://微服务名称
)
路径匹配可以解决不暴露微服务名称,同时将请求转发到对应微服务的问题。
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: http://localhost:9005 # 目标微服务的请求地址和端口
predicates:
# Path路径匹配, 表示以/order/开头的请求都会将其转发到上面配置的uri=http://localhost:9005,即真正的微服务地址上去。这样就可以通过"gatewayhost:port//order/**"去直接访问微服务了,不用暴露微服务名称了
- Path=/order/**
上面的配置,如果访问网关的请求如"localhost:8888/order/findOrderByUserId/1",因为其中包含了Path中定义的"/order",所以这个请求会被转发到uri属性中配置的"http://localhost:9005"上,这是真实微服务的地址。
即初始请求"localhost:8888/order/findOrderByUserId/1
“会被转发到”http://localhost:9005/order/findOrderByUserId/1
"。这样就实现了不暴露微服务名称,而通过路径将微服务转发到真实服务上的功能。
测试:
问题分析:
这样好吗?每一个断言都要配置微服务真实的地址,如果微服务地址变了呢?或者是集群呢?显然在spring.cloud.gateway.routes[0].uri
属性中配置http://localhost:9005
这种微服务地址不是一个很正确的选择。
改进(推荐写法):
整合负载均衡器,使用lb://微服务名称
:
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order # 整合负载均衡器ribbon、loadbalance。 lb:loadBalance, 是gateway提供的一种协议。mall-order是微服务名称,这样也就间接的确定了要调用的微服务地址,还有了负载均衡功能。
predicates:
# Path路径匹配, 表示以/order/开头的请求都会将其转发到上面配置的uri=http://localhost:9005,即真正的微服务地址上去。这样就可以通过"gatewayhost:port//order/**"去直接访问微服务了,不用暴露微服务名称了
- Path=/order/**
整合负载均衡器ribbon、loadbalance。 lb:loadBalance, 是gateway提供的一种协议。mall-order是微服务名称,这样也就间接的确定了要调用的微服务地址,还有了负载均衡功能。
测试:
2.2.2 时间匹配
可以用在限时抢购的一些场景中。
After
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route # 路由ID,全局唯一
uri: lb://mall-order
predicates:
- Path=/order/**
# 测试:http://localhost:8888/order/findOrderByUserId/1
# 匹配在指定的日期时间之后发生的请求。入参是ZonedDateTime类型
- After=2022-12-25T15:28:14.246+08:00[Asia/Shanghai]
注意:spring.cloud.gateway.discovery.locator.enabled
属性默认就为false,因为我们不需要再配置了。而spring.cloud.gateway.enabled
属性默认为true,所以我们也不用配置了。
注意:spring.cloud.gateway.discovery.locator.enabled
属性如果设置为true之后,表示可以根据微服务名称(如mall-order)找到真实的微服务IP:port,然后去调用真正的微服务。这样是不安全的,如果配置成true,也会导致我们后面的断言配置失效!!!所以一定要将其设置为false! 或者直接不配置该属性。
route的组成部分
- id:路由的ID,自定义,全局唯一。
- uri:匹配路由的转发地址,一般配置微服务的真实地址。
- predicates:配置该路由的断言,通过PredicateDefinition类进行接收配置。
- order:路由的优先级,数字越小,优先级越高。
获取ZonedDateTime类型的指定日期时间:
ZonedDateTime zonedDateTime = ZonedDateTime.now();//默认时区
// 用指定时区获取当前时间
ZonedDateTime zonedDateTime2 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
我们设置的时间是2022-12-25之后才能访问,现在发起请求直接报错:
超过设置时间之后再次请求:
Before
spring:
cloud:
gateway:
routes:
- id: before_route
uri: lb://mall-order
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
Between
spring:
cloud:
gateway:
routes:
- id: between_route
uri: lb://mall-order
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
2.2.3 Cookie匹配
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order
predicates:
- Path=/order/**
# 与cookie匹配
- Cookie=username, jihu
postman测试:
2.2.4 Header匹配
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order
predicates:
# Header匹配 请求中带有请求头名为 x-request-id,其值与 \d+ 正则表达式匹配
- Header=X-Request-Id, \d+
测试:
2.2.5 host主机名匹配
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
2.2.6 Http方法类型匹配
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
2.2.7 uri路由匹配
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
2.2.8 远程路由RemoteAddr匹配
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
2.2.9 权重路由
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
2.2.5 自定义路由断言工厂
自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过 exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。
注意: 命名需要以 RoutePredicateFactory
结尾。
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
@Component
@Slf4j
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
public CheckAuthRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
log.info("调用CheckAuthRoutePredicateFactory" + config.getName());
if (config.getName().equals("jihu")) {
return true;
}
return false;
}
};
}
/**
* 快捷配置
*/
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("name");
}
public static class Config {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
yml中配置:
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order
predicates:
# 测试:http://localhost:8888/order/findOrderByUserId/1
- Path=/order/** #Path路径匹配
# 自定义CheckAuth断言工厂(full 配置规则)
# - name: CheckAuth
# args:
# name: jihu
- CheckAuth=jihu # 快捷配置
2.3 过滤器工厂( GatewayFilter Factories)配置
SpringCloudGateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
2.3.1 添加请求头
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order
#配置过滤器工厂
filters:
- AddRequestHeader=X-Request-color, red #添加请求头
predicates:
- Path=/order/**
@GetMapping("/testgateway")
public String testGateway(HttpServletRequest request) throws Exception {
log.info("gateWay获取请求头X-Request-color:"
+request.getHeader("X-Request-color"));
return "success";
}
@GetMapping("/testgateway2")
public String testGateway(@RequestHeader("X-Request-color") String color) throws Exception {
log.info("gateWay获取请求头X-Request-color:"+color);
return "success";
}
测试:
http://localhost:8888/order/testgateway
http://localhost:8888/order/testgateway2
2.3.2 添加请求参数
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order
#配置过滤器工厂
filters:
- AddRequestParameter=color, blue # 添加请求参数
predicates:
- Path=/order/**
@GetMapping("/testgateway3")
public String testGateway3(@RequestParam("color") String color) throws Exception {
log.info("gateWay获取请求参数color:"+color);
return "success";
}
测试:
http://localhost:8888/order/testgateway3
2.3.3 为匹配的路由统一添加前缀
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order
#配置过滤器工厂
filters:
- PrefixPath=/mall-order # 添加前缀 对应微服务需要配置context-path
predicates:
- Path=/order/**
mall-order微服务中需要配置:
server:
servlet:
context-path: /mall-order
测试:
http://localhost:8888/order/findOrderByUserId/1
注意,这个前缀是网管帮我们加的,我们调用APId
2.3.4 重定向操作
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order
#配置过滤器工厂
filters:
- RedirectTo=302, https://www.baidu.com/ #重定向到百度
predicates:
- Path=/order/**
浏览器访问:localhost:8888/order/findOrderByUserId/1
2.3.5 自定义过滤器工厂
继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。
注意类的命名规则!!!要以GatewayFilterFactory
结尾。
@Component
@Slf4j
public class CheckAuthGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
log.info("调用CheckAuthGatewayFilterFactory==="
+ config.getName() + ":" + config.getValue());
return chain.filter(exchange);
};
}
}
配置自定义的过滤器工厂:
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order
#配置过滤器工厂
filters:
- CheckAuth=jihu, 男
predicates:
- Path=/order/**
测试:localhost:8888/order/findOrderByUserId/1
2.4 全局过滤器(Global Filters)配置
GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过, GlobalFilter 会作用于所有路由。
官方声明:GlobalFilter的接口定义以及用法在未来的版本可能会发生变化。
2.4.1 LoadBalancerClientFilter
LoadBalancerClientFilter 会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的值(一个URI),如果该值的scheme是 lb,比如:lb://myservice
,它将会使用Spring Cloud的LoadBalancerClient 来将 myservice 解析成实际的host和port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的内容。
其实就是用来整合负载均衡器Ribbon的。
spring:
cloud:
gateway:
routes:
- id: order_route
uri: lb://mall-order
predicates:
- Path=/order/**
2.4.2 自定义全局过滤器(token检查+IP白名单)
检查token的全局过滤器
@Component
@Order(-1)
@Slf4j
public class CheckAuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//校验请求头中的token
List<String> token = exchange.getRequest().getHeaders().get("token");
log.info("token:"+ token);
if (token.isEmpty()){
return null;
}
// 检验token是否合法
// validateToken(token)
return chain.filter(exchange);
}
}
IP白名单过滤器:
@Component
public class CheckIPFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return 0;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
HttpHeaders headers = exchange.getRequest().getHeaders();
//模拟对 IP 的访问限制,即不在 IP 白名单中就不能调用的需求
if (getIp(headers).equals("127.0.0.1")) {
return null;
}
return chain.filter(exchange);
}
private String getIp(HttpHeaders headers) {
return headers.getHost().getHostName();
}
}
2.5 Gateway跨域配置(CORS Configuration)
2.5.1 通过yml配置的方式
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- DELETE
- PUT
- OPTION
2.5.2 通过java配置的方式
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
2.6 gateway整合sentinel限流
注意:被调用的微服务必须接入sentinel。
建议阅读官网,中文的:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
- route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId。相当于对整个微服务粒度设置限流规则。如我们配置的routeId=order_route,对order_route设置限流规则,相当于对微服务mall-order
- 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
2.6.1 快速开始
使用时需引入依赖:
<!-- gateway网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--sentinel整合springCloudAlibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--Gateway 适配器-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
接入sentinel dashboard,添加yml配置:
server:
port: 8888
spring:
application:
name: mall-gateway
#配置nacos注册中心地址
cloud:
nacos:
discovery:
namespace: mall
server-addr: 192.168.131.172:8848
sentinel:
transport:
# 添加sentinel的控制台地址
dashboard: 127.0.0.1:8080
gateway:
discovery:
locator:
# 默认为false,设为true开启通过微服务创建路由的功能,即可以通过微服务名访问服务
# http://localhost:8888/mall-order/order/findOrderByUserId/1
# 如果mall-order配到某个微服务了,会将http://localhost:8888/mall-order替换成微服务对于的IP地址
enabled: false
# 是否开启网关
enabled: true
routes:
- id: order_route #路由ID,全局唯一
#uri: http://localhost:9005 # 目标微服务的请求地址和端口
uri: lb://mall-order # 整合负载均衡器ribbon、loadbalance。 lb:loadBalance, 提供的一种协议。mall-order是微服务名称,这样也就间接的确定了要调用的微服务地址,甚至有了负载均衡功能。
predicates:
- Path=/order/**
- id: user_route
uri: lb://mall-user
predicates:
- Path=/user/**
使用时只需注入对应的 SentinelGatewayFilter
实例以及 SentinelGatewayBlockExceptionHandler
实例即可:
@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;
}
/**
* 限流异常处理器
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
/**
* 限流过滤器
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
}
用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则:
GatewayConfiguration中添加:
@PostConstruct
public void doInit() {
//初始化自定义的API
initCustomizedApis();
//初始化网关限流规则(代码中配置流控规则,一般在控制台配置)
initGatewayRules();
//自定义限流异常处理器
initBlockRequestHandler();
}
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
// 设置资源保护名
ApiDefinition api = new ApiDefinition("order_service_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
// 设置匹配路径
add(new ApiPathPredicateItem().setPattern("/order/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(api);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
//resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
//count:限流阈值
//intervalSec:统计时间窗口,单位是秒,默认是 1 秒。
rules.add(new GatewayFlowRule("order_route")
.setCount(2)
.setIntervalSec(1)
);
rules.add(new GatewayFlowRule("order_service_api")
.setCount(2)
.setIntervalSec(1)
);
// 加载网关规则
GatewayRuleManager.loadRules(rules);
}
private void initBlockRequestHandler() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
HashMap<String, String> result = new HashMap<>();
result.put("code",String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
result.put("msg", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(result));
}
};
//设置自定义异常处理器
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
2.6.2 网关流控控制台
Sentinel 1.6.3 引入了网关流控控制台的支持,用户可以直接在 Sentinel 控制台上查看 API Gateway 实时的 route 和自定义 API 分组监控,管理网关规则和 API 分组配置。
在 API Gateway 端,用户只需要在原有启动参数的基础上添加如下启动参数即可标记应用为 API Gateway 类型:
# 注:通过 Spring Cloud Alibaba Sentinel 自动接入的 API Gateway 整合则无需此参数
-Dcsp.sentinel.app.type=1
我们访问sentinel:http://localhost:8080/#/dashboard
然后调用order微服务的API,然后就可以看到数据了:
这里有个order_route使我们配置的mall-order微服务对应的路由id。
如果此时我们对这个order_route路由id设置流控规则,也就是对微服务mall-order设置流控规则。
现在将微服务mall-order的QPS设置为了2,然后我们通过网关访问这个微服务:
当访问多次的时候就会出现被限流:
错误消息我们可以在Gateway配置类的流控罪责中设置,也就是我们上面的GatewayConfiguration类中配置。
【推荐使用】自定义 API 分组的监控
在上面的配置中删除GatewayConfiguration
配置类和依赖sentinel-spring-cloud-gateway-adapter
,而是引入如下依赖:
<!--gateway接入sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
再次启动sentinel和网关然后访问order微服务:
可以看到和之前的sentinel界面不一样了。这时候我们再来配置流控规则:
发现通过这种方式功能强大多了,正好满足gateway的限流方式。
routeId级别限流
自定义API组限流
我们先对希望限流的APIfindOrderByUserId
创建一个自定义API组,设置的规则是对参数1和2进行匹配。
然后我们在创建一个API分组级别的流控规则:
然后我们来测试:
http://localhost:8888/order/findOrderByUserId/1
http://localhost:8888/order/findOrderByUserId/2
http://localhost:8888/order/findOrderByUserId/3
发现参数如果是1和2都会被限流,其他的则不会,这正好匹配我们刚才设置的流控规则。
如果想限定更细致的参数,自定义API组的时候可以设置正则验证。
2.6.3 网关流控实现原理
三、网关高可用
为了保证 Gateway 的高可用性,可以同时启动多个 Gateway 实例进行负载,在 Gateway 的上游使用 Nginx 或者 F5 进行负载转发以达到高可用。