Spring Cloud Gateway过滤器配置
我们只是简单的介绍了如何集成Spring Cloud Gateway
,下面我们来详细说说Spring Cloud Gateway
的一些特性
Spring Cloud Gateway 建立在 Spring Boot 2.x、Spring WebFlux 和 Project Reactor 之上。 因此,当您使用 Spring Cloud Gateway 时,您所知道的许多熟悉的同步库(例如 Spring Data 和 Spring Security)和模式可能并不适用。 如果您不熟悉这些项目,我们建议您在使用 Spring Cloud Gateway 之前先阅读他们的文档以熟悉一些新概念。
路由的配置方式
基于配制文件
Spring Cloud Gateway
有两种配置路由和过滤器的方法:快捷方式和完全扩展的参数。下面的大多数示例都使用快捷方式。
快捷方式配置
快捷方式配置由过滤器名称识别,后跟等号 ( =
),后跟以逗号 ( ,
) 分隔的参数值。
spring:
cloud:
gateway:
routes:
- id: after_route #路由的ID,保证全局唯一就行了,建议和服务名搭配命名
uri: https://example.org #uri,断言匹配时转发的uri
predicates:
- Cookie=mycookie,mycookievalue #断言,当断言匹配时就会转发至uri
复制代码
完全扩展的参数
完全扩展的参数看起来更像是带有名称/值对的标准 yaml 配置。通常,会有一把name
钥匙和一把args
钥匙。键是用于配置谓词或过滤器的args
键值对映射。
spring:
cloud:
gateway:
routes:
- id: after_route #路由的ID,保证全局唯一就行了,建议和服务名搭配命名
uri: https://example.org #uri,断言匹配时转发的uri
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue
复制代码
之前我们项目中用到的路由匹配规则就是快捷方式
service-url:
user-service: http://localhost:8080
spring:
cloud:
gateway:
# 静态路由
routes:
- id: path_route
uri: ${service-url.user-service}/user/get/{id}
predicates:
- Path=/user/get/{id}
复制代码
这个配置的意思就是当请求的URI
与/user/get/{id}
相匹配时,就会将请求转发至http://localhost:8080/user/get/{id}
基于Java代码的配置方式
除了可以通过配制文件的方式,我们还可以通过Java代码进行硬编码,但是我个人不推荐这种方式,因为一是我认为在配制文件中配置路由可读性更高,另外就是配制文件可以整合配置中心实现动态路由,而显然,Java硬编码的方式是没法配合配置中心进行动态路由的,灵活性不高
// Spring 官方的例子
@SpringBootApplication
public class DemogatewayApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
.uri("http://httpbin.org"))
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything/**")
.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
.uri("http://httpbin.org"))
.build();
}
}
复制代码
网上Copy的代码
package com.atguigu.springcloud.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author LiFang
* @version 1.0
* @since
@Configuration
public class GateWayConfig {
/**
* @param routeLocatorBuilder
* @return org.springframework.cloud.gateway.route.RouteLocator
* @description 配置了一个id为route_name的路由规则,
* 当访问地址为http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
* @author LiFang
* @date
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_atguigu",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("path_route_atguigu2",
r -> r.path("/guoji")
.uri("http://news.baidu.com/guoji")).build();
return routes.build();
}
}
复制代码
过滤器
Route Predicate 的使用
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。 Spring Cloud Gateway包括许多内置的Route Predicate工厂。 所有这些Predicate都与HTTP请求的不同属性匹配。 多个Route Predicate工厂可以进行组合,下面我们来介绍下一些常用的Route Predicate。
注意:Predicate中提到的配置都在application-predicate.yml文件中进行修改,并用该配置启动api-gateway服务。
Spring Cloud Gateway自带了很多断言过滤器,可以让我们在断言中使用,我这里只简单的列举几个常用的,想要详细了解的可以查阅官方文档
After Route Predicate
在指定时间之后的请求会匹配该路由。
spring:
cloud:
gateway:
routes:
- id: after_route
uri: ${service-url.user-service}
predicates:
- After=2019-09-24T16:30:00+08:00[Asia/Shanghai]
复制代码
Before Route Predicate
在指定时间之前的请求会匹配该路由。
spring:
cloud:
gateway:
routes:
- id: before_route
uri: ${service-url.user-service}
predicates:
- Before=2019-09-24T16:30:00+08:00[Asia/Shanghai]
复制代码
Between Route Predicate
在指定时间区间内的请求会匹配该路由。
spring:
cloud:
gateway:
routes:
- id: before_route
uri: ${service-url.user-service}
predicates:
- Between=2019-09-24T16:30:00+08:00[Asia/Shanghai], 2019-09-25T16:30:00+08:00[Asia/Shanghai]
复制代码
Cookie Route Predicate
带有指定Cookie的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: ${service-url.user-service}
predicates:
- Cookie=username,macro
复制代码
带有cookie为username=macro
的请求可以匹配该路由
Header Route Predicate
带有指定请求头的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: header_route
uri: ${service-url.user-service}
predicates:
- Header=X-Request-Id, \d+
复制代码
带有请求头为X-Request-Id:123
的请求可以匹配该路由
Method Route Predicate
发送指定方法的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: method_route
uri: ${service-url.user-service}
predicates:
- Method=GET
复制代码
GET请求可以匹配该路由
Path Route Predicate
发送指定路径的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: path_route
uri: ${service-url.user-service}/user/{id}
predicates:
- Path=/user/{id}
复制代码
Query Route Predicate
带指定查询参数的请求可以匹配该路由
spring:
cloud:
gateway:
routes:
- id: query_route
uri: ${service-url.user-service}/user/getByUsername
predicates:
- Query=username
复制代码
带username=macro
查询参数的请求可以匹配该路由。
RemoteAddr Route Predicate
从指定远程地址发起的请求可以匹配该路由
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: ${service-url.user-service}
predicates:
- RemoteAddr=192.168.1.1/24
复制代码
从192.168.1.1发起请求可以匹配该路由
Weight Route Predicate
使用权重来路由相应请求,以下表示有80%的请求会被路由到localhost:8201,20%会被路由到localhost:8202
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: http://localhost:8201
predicates:
- Weight=group1, 8
- id: weight_low
uri: http://localhost:8202
predicates:
- Weight=group1, 2
复制代码
Route Filter 的使用
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生,下面我们介绍下常用路由过滤器的用法。
AddRequestHeader
AddRequestHeader GatewayFilter 采用名称和值参数。 以下示例配置一个 AddRequestHeader GatewayFilter:
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-red, blue
复制代码
这个例子将 X-Request-red:blue 标头添加到所有匹配请求的下游请求标头中
AddRequestParameter GatewayFilter
给请求添加参数的过滤器
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: http://localhost:8201
filters:
- AddRequestParameter=username, macro
predicates:
- Method=GET
复制代码
以上配置会对GET请求添加username=macro
的请求参数,相当于发起该请求:
curl http://localhost:8201/user/getByUsername?username=macro
复制代码
StripPrefix GatewayFilter
对指定数量的路径前缀进行去除的过滤器
spring:
cloud:
gateway:
routes:
- id: strip_prefix_route
uri: http://localhost:8201
predicates:
- Path=/user-service/**
filters:
- StripPrefix=2
复制代码
以上配置会把以/user-service/
开头的请求的路径去除两位,通过curl工具使用以下命令进行测试
curl http://localhost:9201/user-service/a/user/1
复制代码
相当于发起该请求:
curl http://localhost:8201/user/1
复制代码
PrefixPath GatewayFilter
与StripPrefix过滤器恰好相反,会对原有路径进行增加操作的过滤器。
spring:
cloud:
gateway:
routes:
- id: prefix_path_route
uri: http://localhost:8201
predicates:
- Method=GET
filters:
- PrefixPath=/user
复制代码
以上配置会对所有GET请求添加/user
路径前缀,通过curl工具使用以下命令进行测试。
curl http://localhost:9201/1
复制代码
相当于发起该请求:
curl http://localhost:8201/user/1
复制代码
Hystrix GatewayFilter
Hystrix 过滤器允许你将断路器功能添加到网关路由中,使你的服务免受级联故障的影响,并提供服务降级处理。
- 要开启断路器功能,我们需要在pom.xml中添加Hystrix的相关依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
复制代码
- 然后添加相关服务降级的处理类:
@RestController
public class FallbackController {
@GetMapping("/fallback")
public Object fallback() {
Map<String,Object> result = new HashMap<>();
result.put("data",null);
result.put("message","Get request fallback!");
result.put("code",500);
return result;
}
}
复制代码
- 在application-filter.yml中添加相关配置,当路由出错时会转发到服务降级处理的控制器上:
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: http://localhost:8201
predicates:
- Method=GET
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
复制代码
- 关闭user-service,调用该地址进行测试:http://localhost:9201/user/1 ,发现已经返回了服务降级的处理信息。
RequestRateLimiter GatewayFilter
RequestRateLimiter 过滤器可以用于限流,使用RateLimiter实现来确定是否允许当前请求继续进行,如果请求太大默认会返回HTTP 429-太多请求状态。
- 在pom.xml中添加相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
复制代码
- 添加限流策略的配置类,这里有两种策略一种是根据请求参数中的username进行限流,另一种是根据访问IP进行限流
@Configuration
public class RedisRateLimiterConfig {
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
}
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
}
复制代码
- 我们使用Redis来进行限流,所以需要添加Redis和RequestRateLimiter的配置,这里对所有的GET请求都进行了按IP来限流的操作
server:
port: 9201
spring:
redis:
host: localhost
password: 123456
port: 6379
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: http://localhost:8201
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 #每秒允许处理的请求数量
redis-rate-limiter.burstCapacity: 2 #每秒最大处理的请求数量
key-resolver: "#{@ipKeyResolver}" #限流策略,对应策略的Bean
predicates:
- Method=GET
logging:
level:
org.springframework.cloud.gateway: debug
复制代码
Retry GatewayFilter
对路由请求进行重试的过滤器,可以根据路由请求返回的HTTP状态码来确定是否进行重试。
- 修改配置文件:
spring:
cloud:
gateway:
routes:
- id: retry_route
uri: http://localhost:8201
predicates:
- Method=GET
filters:
- name: Retry
args:
retries: 1 #需要进行重试的次数
statuses: BAD_GATEWAY #返回哪个状态码需要进行重试,返回状态码为5XX进行重试
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
复制代码
- 当调用返回500时会进行重试,访问测试地址:http://localhost:9201/user/111
- 可以发现user-service控制台报错2次,说明进行了一次重试。
2019-10-27 14:08:53.435 ERROR 2280 --- [nio-8201-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at com.macro.cloud.controller.UserController.getUser(UserController.java:34) ~[classes/:na]
复制代码
添加默认过滤器
要添加过滤器并将其应用于所有路由,您可以使用spring.cloud.gateway.default-filters
. 此属性采用过滤器列表。以下清单定义了一组默认过滤器:
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin
复制代码
自定义全局过滤器
当请求与路由匹配时,过滤 Web 处理程序会将 的所有实例GlobalFilter
和所有特定于路由的实例添加GatewayFilter
到过滤器链中。这个组合的过滤器链是按org.springframework.core.Ordered
接口排序的,你可以通过实现getOrder()
方法来设置。
由于 Spring Cloud Gateway 区分过滤器逻辑执行的“前”和“后”阶段(请参阅它的工作原理),具有最高优先级的过滤器是“前”阶段的第一个和“后”阶段的最后一个阶段。
参考示例
package cuit.epoch.pymjl.filter;
import lombok.extern.log4j.Log4j2;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @author Pymjl
* @version 1.0
* @date
@Component
@Log4j2
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("=====================come in MyGlobalFilter==========================");
String username = exchange.getRequest().getQueryParams().getFirst("username");
if (username == null) {
log.error("用户名为null,非法用户");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 得到权重,order越小,优先级越高
*
* @return
@Override
public int getOrder() {
return 0;
}
}
复制代码
我们启动项目进行测试
当带上username参数时访问正常
当不带username参数时网关会路由失败
观察后台输出
跨域过滤器配置
您可以配置网关来控制 CORS 行为
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET
复制代码
在前面的示例中,允许来自docs.spring.io
所有 GET 请求路径的请求的 CORS 请求。
要为某些网关路由谓词未处理的请求提供相同的 CORS 配置,请将spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping
属性设置为true
.
通常的配置是:
spring:
cloud:
gateway:
default-filters:
# gateway 和下游系统同时支持了跨域,都会向 response header 中增加 Access-Control-Allow-Origin*,所以出现重复的 *。所以需要使用DedupeResponseHeader 来处理一下响应头重复的问题
- DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE
globalcors:
cors-configurations:
'[/**]':
# 允许携带认证信息
allow-credentials: true
# 允许跨域的源(网站域名/ip),设置*为全部
allowedOrigins: "*"
# 允许跨域的method, 默认为GET和OPTIONS,设置*为全部
allowedMethods: "*"
# 允许跨域请求里的head字段,设置*为全部
allowedHeaders: "*"
复制代码
至此,关于Spring Cloud Gateway的进一步解释到这里就结束啦,后面我们会继续介绍Spring Cloud Gateway的动态路由配置以及负载均衡