Spring Cloud(二十):Gateway 路由匹配表达式工厂,过滤器工厂,全局过滤器,HTTP头过滤器
Spring Cloud Gateway 项目提供了一个基于 Spring 生态体系构建的 API 网关,包括:Spring 5,Spring Boot 2。
Spring Cloud Gateway 旨在提供一种简单而有效的方试将前端请求 URI 路由到后端服务的接口,它还提供了其它的附加实用的功能,例如:安全性,监控/指标,可伸缩性等。
创建 Spring Cloud Gateway 网关服务,需要引入 spring-cloud-starter-gateway 依赖。下面是 Maven 项目中的 pom.xml 添加 Gateway 依赖。
1 | <dependency> |
如果引入了依赖,但又不想启用,可设置:
1 | spring.cloud.gateway.enabled=false |
注意事项:
Spring Cloud Gateway 基于 Spring Boot 2.x,Spring WebFlux 和 Project Reactor 构建。 因此,当使用 Spring Cloud Gateway 时,许多熟悉的同步库(例如,Spring Data 和 Spring Security)和模式(patterns )可能不适用。 若不熟悉这些项目,建议在使用 Spring Cloud Gateway 之前先阅读它们的文档以熟悉一些新概念。
Spring Cloud Gateway 需要 Spring Boot 和 Spring Webflux 提供的 Netty 运行环境。它不是在传递的 Servlet 容器中 或 作为构建成的 WAR 包使用。
即 Spring Cloud Gateway 与
spring-boot-starter-web(Spring MVC)不兼容,需要移出此 WEB 库,否则服务启动会报异常。
Gateway工作方式
下图从总体上概述了 Spring Cloud Gateway 的工作方式:

客户端向 Spring Cloud Gateway 发出请求。
如果网关处理器映射(Gateway Handler Mapping)确定请求与路由匹配,则将其发送到网关 Web 处理器( Gateway Web Handler)。
网关 Web 处理器通过特定于请求的过滤器链来执行请求处理。
过滤器由虚线分隔的原因是,过滤器可以在发送代理请求之前和之后运行逻辑。 所有前置过滤器逻辑都会被执行,然后发出代理请求,后端服务接收并处理请求后返回响应,Gateway 将运行后置过滤器逻辑。
从 Gateway 的工作方式可以看出,其核心逻辑是各种类型的过滤器组合成过滤器链,在过滤器链上传递请求并做相应处理,过滤器链是设计模式中的 职责链模式 的经典应用。
注意:若在路由中定义的 URI 没有指定端口,会使用 HTTP 默认的 80 端口,或 HTTPS 默认 443 端口 。
路由与过滤器配置方式
配置匹配(判断)表达式和和过滤器有 2 种方式:分别是 快捷配置 和 完全扩展配置,而大多数使用快捷配置。
名称和属性名称将与代码中的属性名或子属性名匹配,参数通常按快捷方式配置的顺序列出。
快捷配置
快捷配置是通过滤器名来识别,后跟一个等号(=),然后由逗号分隔的多个参数值(,)
application.yml
1 | spring: |
上面示例定义了 Cookie 路由判断表达式工厂,包含了 2 个参数,Cookie 名是 mycookie ,值将与 mycookievalue 匹配。
完全扩展配置
完全扩展的参数看起来更像带有 键 / 值 (key / value)对的标准 Yaml 配置。 通常会有一个name 键和一个args键(key)。 args键(key)是用于配置判断表达式或过滤器的键值对的 map(映射)。
application.yml
1 | spring: |
此示例是上面快捷配置 Cookie 判断表达式的完整配置。
路由判断表达式工厂
Spring Cloud Gateway 将路由匹配作为 Spring WebFlux HandlerMapping 基础架构的一部分。
Spring Cloud Gateway 包括许多内置的路由判断表达式工厂(已自动配置为 Bean)。 所有这些判断表达式都与 HTTP 请求的不同属性匹配。 也可以将多个路由判断表达式工厂按一定的逻辑(logical)和声明(statements)组合使用。
After 时间路由匹配
After路由判断表达式工厂使用参数名为 datetime(Java ZonedDateTime 类型) 来接收日期时间值。此判断表达式将匹配指定日期时间 之后 的请求。示例如下:
application.yml
1 | spring: |
路由将匹配在 Jan 20, 2017 17:42 Mountain Time (Denver) 日期时间之后的所有请求。
Before 时间路由匹配
Before路由判断表达式工厂使用参数名为 datetime(Java ZonedDateTime 类型) 来接收日期时间值。此判断表达式将匹配指定日期时间 之前 的请求。示例如下:
application.yml
1 | spring: |
路由将匹配在 Jan 20, 2017 17:42 Mountain Time (Denver) 日期时间之前的所有请求。
Between 时间路由匹配
Between 路由判断表达式工厂使用 datetime1 和 datetime2 参数(Java ZonedDateTime)接收开始和结束日期时间。此判断表达式将匹配在这两个时间范围内的所有请求。datetime2 必须在 datetime1 之后。示例如下:
application.yml
1 | spring: |
此路由将匹配在 Jan 20, 2017 17:42 Mountain Time (Denver) 之后,Jan 21, 2017 17:42 Mountain Time (Denver) 之前的所有请求。这对要维护某一窗口期非常有用。
Cookie 路由匹配
Cookie路由判断表达式工厂使用 2 个参数,即 cookie name 和 regexp(Java 正则表达式),此判断表达式将匹配给定的name且其值与正则表达式匹配的 cookie。示例如下:
application.yml
1 | spring: |
此示例将匹配 name 为 chocolate,且值匹配 ch.p 正则表达式的 cookie。
Header 路由匹配
Header请求头路由判断表达式工厂使用两个参数: name 和 regexp(Java 正则表达式),此判断表达式将匹配给定的name且其值与正则表达式匹配的 header。示例如下:
application.yml
1 | spring: |
示例中的路由将匹配持有头名称为 X-Request-Id的请求,且值与 \d+正则表达式匹配(值为一个或多个数字)。
Host 路由匹配
Host主机路由判断表达式工厂使用一个名为 patterns的参数,类型是 list ,值是带有点号( .)作为分隔符的 Ant-style模式。
该判断表达式会匹配与模式匹配的 Host 头。下面示例一个配置 Host 路由的判断表达式:
application.yml
1 | spring: |
此示例路由将匹配持有请求头存在 Host 头,值为 www.somehost.org 或 beta.somehost.org 或 www.anotherhost.org 的请求。
也支持 URI 模板变量(例如 {sub}.myhost.org),判断表达式将 提取 URI 模板变量(如上例中定义的 sub)作为名称和值(names / values)的映射,并使用 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE中定义的键将其放置在ServerWebExchange.getAttributes()中。 这些值可供 GatewayFilter factories 使用。
Method 路由匹配
Method方法路由判断表达式工厂使用名为 methods 的参数,其可以有一个或多个匹配 HTTP 方法的的值。如下示例:
application.yml
1 | spring: |
此示例路由会匹配 GET 或 POST 请求。
Path 路由匹配
Path路由判断表达式使用两个参数:Spring PathMatcher patterns列表(list)和一个称为matchOptionalTrailingSeparator的可选标志。如下示例:
application.yml
1 | spring: |
此示例路由匹配请求路径为,例如:/red/1 或 /red/blue 或 /blue/gree。
该路由判断表达式提取 URI 模板变量(例如,segment)作为 名称 和 值 的映射(map),并使用ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE中定义的键将其放置在ServerWebExchange.getAttributes()中。 这些值可供 GatewayFilter factories 使用。
ServerWebExchange.getAttributes()返回的是一个属性 Map,直接使用 get 方法获取 URI 模板变量。如下示例:
1 | Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange); |
Query 路由匹配
Query路由表达式工厂使用两个参数:一个必需的param 和 一个可选的 regexp(Java 正则表达式)。如下示例:
application.yml
1 | spring: |
此示例路由会匹配包含有 green查询参数的请求。
application.yml
1 | spring: |
此表达式路由匹配包含 red 查询参数,且值与 gree. 正则表达式匹配的请求。所以 green 和 greet 都会匹配到。
RemoteAddr 路由匹配
RemoteAddr 远程 IP 地址路由判断表达式使用一个名为 sources 的 List 来装值,这些值是 CIDR 表示法(IPv4 或 IPv6)字符串。例如 192.168.0.1/16(其中 192.168.0.1 是 IP 地址,16 是子网掩码)。如下示例:
application.yml
1 | spring: |
路由将匹配请求的源地址,例如,192.168.1.10。
修改远程地址解析方式
默认,RemoteAddr 路由判断表达式工厂使用请求中携带的远程地址,如果 Spring Cloud Gateway 部署在一个代理层的后面,则这种方式可能无法匹配到真实的客户端 IP 地址。
可以通过设置自定义的 RemoteAddressResolver来自定义解析远程地址的方式。SpringCloudGateway 提供了一个非默认的远程地址解析器,它基于 X-Forwarded-For头 和 xForwardedRemoteAddResolver。
XForwardedRemoteAddressResolver 有两个静态构造方法,分别采用不同的安全方式:
XForwardedRemoteAddressResolver::trustAll返回一个RemoteAddressResolver,它总是使用在X-Forwarded-For头中发现的第一个 IP 地址。此方法易受欺骗,因为恶意客户端可能为 X-Forwarded-for 设置初始值,解析器将接受该值。XForwardedRemoteAddressResolver::maxTrustedIndex获取一个索引,该索引与在 Spring Cloud Gateway 前面运行的受信任基础结构的数量相关。例如,如果 Spring Cloud Gateway 只能通过 HAProxy 访问,则应使用值 1。如果在访问 Spring Cloud Gateway 之前需要两跳可信基础设施,则应使用值 2。
考虑下面的头值:
1
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3
maxTrustedIndex值将使用对应的远程地址:maxTrustedIndex result [ Integer.MIN_VALUE,0](invalid, IllegalArgumentExceptionduring initialization)1 0.0.0.3 2 0.0.0.2 3 0.0.0.1 [4, Integer.MAX_VALUE]0.0.0.1 以下示例显示了如何使用 Java 实现相同的配置:
1
2
3
4
5
6
7
8
9RemoteAddressResolver resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1);
...
.route("direct-route",
r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24")
.uri("https://downstream1")
.route("proxied-route",
r -> r.remoteAddr(resolver, "10.10.1.1", "10.10.1.1/24")
.uri("https://downstream2")
)
Weight 路由匹配
Weight 权重路由使用两个参数:group 和 weight(int 型),每组会计算权重。如下示例:
1 | spring: |
此示例路由会将 80% 的流量转发到 weighthigh.org,20% 的流量转发到 weightlow.org。网关的 权重路由
GatewayFilter 工厂
路由过滤器允许以某种方式修改传入的 HTTP请求 或传出的 HTTP响应。 路由过滤器的作用域是特定路由,Spring Cloud Gateway 提供了许多内置的 GatewayFilter Factories(网关过滤器工厂)。
**备注:**更多关于如何使用下面系列过滤器的详细示例,可参考单元测试(unit tests)。
AddRequestHeader
AddRequestHeader 添加请求头 GatewayFilter工厂使用 name 和 value 参数。如下示例:
application.yml
1 | spring: |
此示例将 X-Request-red:blue头添加到所有匹配要转发到下游服务的请求头中。
AddRequestHeader 知道用于匹配路径(path)或主机(host)的 URI 变量。 URI 变量可以在值中使用,并在运行时扩展。 如下示例:
application.yml
1 | spring: |
AddRequestParameter
AddRequestParameter添加请求参数GatewayFilter工厂使用 name 和 value 参数。如下示例:
application.yml
1 | spring: |
示例将为所有匹配转发给下游的请求的查询字符串(request’s query string)添加red=blue。
AddRequestParameter 知道用于匹配路径(path)或主机(host)的 URI 变量。 URI 变量可以在值中使用,并在运行时扩展。 如下示例:
application.yml
1 | spring: |
AddResponseHeader
AddResponseHeader 添加响应头GatewayFilter工厂使用 name 和 value 参数。如下示例:
application.yml
1 | spring: |
此示例将 X-Response-Foo:Bar头添加到所有匹配请求的下游响应头中。
AddResponseHeader 知道用于匹配路径(path)或主机(host)的 URI 变量。 URI 变量可以在值(value)中使用,并在运行时扩展。 如下示例:
application.yml
1 | spring: |
DedupeResponseHeader
DedupeResponseHeader移除重复的响应头 GatewayFilter 工厂使用一个 name参数和一个可选的strategy参数,name可以包含以空隔为分隔符的头名称列表。如下示例:
application.yml
1 | spring: |
此示例将删除 Access-Control-Allow-Credentials 和 Access-Control-Allow-Origin 响应头中的重复值,如果在网关 CORS 逻辑和下游服务逻辑都添加它们情况下。
DedupeResponseHeader过滤器也能接收一个可选参数strategy,它接受的值有 RETAIN_FIRST (default), RETAIN_LAST, 和RETAIN_UNIQUE。
Hystrix
注意:Netflix 将 Hystrix 置于维护模式。建议使用带有 Resilience4J 的 Spring Cloud CircuitBreaker Gateway Filter,因为 Hystrix 的支持将在未来的版本中删除。
Hystrix 是 Netflix 实现 circuit breaker pattern (熔断器模式)的一个库。Hystrix GatewayFilter 允许将熔断器引入网关路由,保护的服务不受级联故障的影响,并允许在下游故障时提供回退响应。
要在项目中启用 Hystrix GatewayFilter 实例,需要添加来自 Spring Cloud Netflix 的依赖spring-cloud-starter-netflix-hystrix。
Hystrix GatewayFilter 工厂需要一个 name 参数,即 HystrixCommand 的名称。示例如下:
application.yml
1 | spring: |
此示例将其余过滤器包装在命令名称为 myCommandName 的 HystrixCommand 中。
Hystrix 过滤器还可以接受可选参数 fallbackUri 。目前,只支持 forward:方式,如果回调被调用,请求会被转发到匹配 URI 的 Controller。示例如下:
application.yml
1 | spring: |
Hystrix 回调被调用时,将转发到/incaseoffailureusethis URI。注意,这个示例还演示了(可选的)Spring Cloud Netflix Ribbon load-balancing(在目标URI上定义了 lb 前缀)。
主要场景是将fallbackUri用于网关应用程序中的内部控制器或处理程序。但是,也可以将请求重新路由到外部应用程序中的控制器或处理程序。如下所示:
application.yml
1 | spring: |
在此示例中,网关应用程序中没有fallback端点或处理程序。但是,在另一个应用程序中有一个是在 localhost:9994 的回调端点。
在请求被转发到回退(fallback)的情况下,Hystrix 网关过滤器还提供导致该请求的Throwable。它作为ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR属性添加到ServerWebExchange中,在处理网关应用程序中的回退时可以使用该属性。
或者在外部控制器 / 处理程序场景中,可以添加带有异常详细信息的头。更多信息可参考 FallbackHeaders GatewayFilter Factory section.
可以使用全局默认值配置 Hystrix 设置(如 超时),也可以使用应用程序属性逐个路由配置 Hystrix 设置,如 Hystrix wiki 上所述。
为上面的示例设置 5 秒的路由超时,可以使用如下配置:
1 | hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000 |
Spring Cloud CircuitBreaker
Spring Cloud CircuitBreaker GatewayFilter 工厂使用 Spring Cloud CircuitBreaker APIs 将 Gateway 路由包装在熔断器中(circuit breaker)。
Spring Cloud CircuitBreaker 支持可与 Spring Cloud Gateway 一起使用的两个库 Hystrix 和 Resilience4J。 由于Netflix 已将 Hystrix 置于仅维护模式,因此建议使用 Resilience4J。
要启用 Spring Cloud CircuitBreaker,需要引入 spring-cloud-starter-circuitbreaker-reactor-resilience4j 或 spring-cloud-starter-netflix-hystrix 依赖。配置如下示例:
application.yml
1 | spring: |
要配置断路器,请参阅所使用的基础断路器实现的配置。
Spring Cloud CircuitBreaker 过滤器也可以接收可选参数 fallbackUri 参数。目前,只支持 forward: 方式的 URI。如果执行了回退,请求会被转发到匹配 URI 的控制器(Controller)。如下示例回退转发:
application.yml
1 | spring: |
上面示例的 Java 实现:
1 |
|
上面示例:当熔断器回退被调用时,将转发请求到 /inCaseOfFailureUseThis URI。 请注意,此示例还演示了(可选)Spring Cloud Netflix Ribbon load-balancing(负载平衡)(由目标 URI 上的 lb 前缀定义)。
主要场景是使用 fallbackUri 在网关应用程序内定义内部控制器或处理程序。 但是,还可以将请求重新路由到外部应用程序中的控制器或处理程序,如下所示:
application.yml
1 | spring: |
此示例,网关应用没有 fallback 端点,但在另一个注册 localhost:9994 的应用有回退端点。
如果出现了请求被转发到回退(回调),则 Spring Cloud CircuitBreaker Gateway 过滤器还会提供引发该请求的Throwable。 它作为 ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR 属性添加到ServerWebExchange,可在处理网关应用程序中的回退时使用。
对于外部的控制器/处理器(controller/handler)场景,可以添加带有异常详细信息的头。见下面章节。
FallbackHeaders
FallbackHeaders 工厂允许在转发到外部应用程序中的 fallbackUri的请求头中添加 Hystrix或Spring Cloud CircuitBreaker执行异常详细信息。如下所示:
application.yml
1 | spring: |
在此示例中,在运行断路器时发生执行异常之后,该请求将转发到在 localhost:9994上运行的应用程序中的fallback端点或处理程序。带有异常类型、消息和(如果可用)根原因异常类型的头数据将由 FallbackHeaders 过滤器添加到请求中。
FallbackHeadersGatewayFilterFactory 有个静态内部类 Config,可以通过设置以下参数的值来重写配置中的头的名称,括号中是默认值。
- executionExceptionTypeHeaderName (Execution-Exception-Type)
- executionExceptionMessageHeaderName (Execution-Exception-Message)
- rootCauseExceptionTypeHeaderName (Root-Cause-Exception-Type)
- rootCauseExceptionMessageHeaderName (Root-Cause-Exception-Message)
MapRequestHeader
MapRequestHeader 请求头映射,使用 fromHeader 和 toHeader 参数接收。它创建一个新命名的头(toHeader),并从传入的 HTTP 请求的现有命名头(fromHeader) 中提取值。
如果输入的 头 不存在,则过滤器不起作用;如果新命名的头已存在,则它的值将使用新值进行扩展。如下示例:
application.yml
1 | spring: |
将X-Request-Red:<values>头添加到转发给下游服务的请求中,其值来自传入的 HTTP请求的 Blue头的值。
PrefixPath
PrefixPath增加路径前缀,使用单个 prefix 参数接收。示例如下:
application.yml
1 | spring: |
示例是给所有匹配请求的路径加上/mypath 前缀。一个 /hello 的请求会被发送到 /mypath/hello。
PreserveHostHeader
PreserveHostHeader GatewayFilter factory 没有参数。此过滤器设置一个路由过滤器检查的请求属性,以确定是否发送请求头中的主机Host信息,而不是由 HTTP 客户端确定的主机头。
application.yml
1 | spring: |
设置了 PreserveHostHeader过滤器,过滤器会把 ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE = true保存到 ServerWebExchange的属性中(attributes:是个 Map 结构)。在 NettyRoutingFilter 过滤器中,会取出该属性,判断值为 true,就取出原始请求头中的 Host 添加进新的请求(request)中。
RequestRateLimiter
RequestRateLimiter请求限流,使用RateLimiter的实现来决定当前请求是否允许被处理,如果否,返回HTTP 429 - Too Many Requests。
过滤器会使用一个可选参数 keyResolver 和 指定的限流参数。keyResolver是一个实现 keyResolver 接口的 Bean,在配置中,通过 name 使用 SpEL来引用 Bean。例如,#{@myKeyResolver} 是一个 SpEL 表达式,引用 name 为 myKeyResolver 的 Bean。下面是 KeyResolver 接口:
KeyResolver.java
1 | public interface KeyResolver { |
KeyResolver 接口允许可插拔策略派生用于限制请求的密钥。 在未来的里程碑版本中,将有一些 KeyResolver 实现。
KeyResolver 的默认实现是 PrincipalNameKeyResolver,它会从 ServerWebExchange检索 Principal并调用 Principal.getName()。
默认情况下,如果 KeyResolver 找不到 Key,请求就会被拒绝。也可以通过设置来调整此行为:
1 | spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key=true|flase |
RequestRateLimiterGatewayFilterFactory.java
1 | /** |
注意:RequestRateLimiter 不支持快捷方式的配置。下面示例的配置是无效的:
application.properties
1 | # INVALID SHORTCUT CONFIGURATION |
Redis RateLimiter 限流
Redis RateLimiter 的实现是基于在 Stripe 完成的工作。它需要引入 spring-boot-starter-data-redis-reactive Spring Boot starter 包。
算法使用的是令牌桶算法(Token Bucket Algorithm)
- redis-rate-limiter.replenishRate 属性:允许每秒可以处理的请求数,没有任何丢弃的请求。该值是令牌桶的流入速率。
- redis-rate-limiter.burstCapacity 属性:允许在一秒内执行的最大请求数。该值是令牌桶持有的令牌数。设置值为 0 将阻止所有请求。
- redis-rate-limiter.requestedTokens 属性:一个请求需要消费的令牌数。这是每个请求从 bucket 中获取的令牌数,默认为 1。
通过设置 replenishRate 和 burstCapacity 相同的值可以实现固定的速率。通过将burstCapacity设置为高于 replenishRate,可以允许临时突发请求流量。
注意:两次突发流量应间隔一段时间,以便于令牌桶中有多余的令牌供突发请求使用,若是连续的突发流量,令牌耗尽则会丢弃请求(返回 HTTP 429 - Too Many Request)。下面列了 redis-rate-limiter 的配置。
application.yml
1 | spring: |
配置示例:令牌产生速度是每秒 10 个,桶中可保存 20 个令牌(能处理的突发请求数,下一秒则只能处理 10 个请求),每个请求消耗 1 个令牌。
如果设置 replenishRate=1,requestedTokens=60,burstCapacity=60,将导致每分钟只能处理 1 个请求(1 request/min)。因为一个请求消耗 60 个令牌,生成 60 个令牌需要 1 分钟。
KeyResolver 的 Java 配置:
1 |
|
也可以定义一个实现 RateLimiter接口的限流器,并注册为 Bean。配置中,可引用使用了 SpEL 的 Bean 名。#{@RateLimiter} 是一个 SpEL 表达式,引用名为 myRateLimiter 的 Bean。
下面配置定义了一个使用上面定义的 keyrolver 的速率限流器:
application.yml
1 | spring: |
RedirectTo
RedirectTo 重定向到,使用 2 个参数:status 和 url。
- status: HTTP 的 300 系统的重定向编码,例如 301。
- url:一个有效的 URL,即是
Location头的值。
对于相对重定向,路由定义的 URI 应使用 uri: no://op。
下面配置示例:
application.yml
1 | spring: |
这将发送一个状态码为 302,Location:https://acme.org 头(header)以执行重定向。
RemoveRequestHeader
RemoveRequestHeader 移除请求头,使用一个 name 参数,以指标要移除的头名称。如下示例:
application.yml
1 | spring: |
示例在转发请求到下游服务之前移除 X-Request-Foo 请求头。
RemoveResponseHeader
RemoveResponseHeader 移除响应头,使用 name 参数,以指示要移除的头名称。示例如下:
application.yml
1 | spring: |
示例是在将响应发送到网关客户端之前,从响应中移除 X-Response-Foo头。
若要移除任何类型的敏感头,应该为任何路由配置此过滤器。此外,还可以使用spring.cloud.gateway.default-filters 配置此过滤器,并将其应用于所有路由。
RemoveRequestParameter
RemoveRequestParameter 移除请求参数,使用 name 参数,以指示要移除的查询参数。示例如下:
application.yml
1 | spring: |
示例,在转发请求到下游服务之前移除请求中的 red 参数。
RewritePath
RewritePath 重写路径,使用 regexp 和 replacement参数。这里使用 Java 的正则表达式提供一种灵活的方式来重写请求路径。如下示例:
application.yml
1 | spring: |
示例,对于 /red/blue 请求路径,在发送请求到下游服务之前将重写路径为 /blue。注意: $ 符应使用 $\,因为这是 YAML 特定的。
RewriteLocationResponseHeader
RewriteLocationResponseHeader 重写响应头中 Location 的值,通常是为了摆脱后端特定的细节,使用 stripVersionMode, locationHeaderName, hostValue 和 protocolsRegex 参数来接收值。如下示例:
application.yml
1 | spring: |
例如,一个 POST api.example.com/some/object/name请求,其响应头 Location 的值是 object-service.prod.example.net/v2/some/object/id 会被重写为 api.example.com/some/object/id。
stripVersionMode 参数:可能有以下的值:
NEVER_STRIP,AS_IN_REQUEST(默认),和ALWAYS_STRIP。- NEVER_STRIP:不剥离版本号(version),即使原始请求路径不包含版本号。
- AS_IN_REQUEST:仅当原始请求路径不包含版本号时,才剥离该版本号。
- ALWAYS_STRIP:始终剥离版本号,即使原始请求路径包含版本号。
hostValue 参数:(如果提供)用于替换响应
Location头的host:port部分。 如果未提供,则使用请求Host头的值。protocolsRegex 参数:必须是一个有效的正则表达式字符串(String),用于匹配协议名称。如果不匹配,则过滤器不执行任何操作。默认为
http | https | ftp | ftps。
RewriteResponseHeader
RewriteResponseHeader重写响应头,使用 name, regexp 和 replacement 参数接收值。使用 Java 正则表达式提供一种灵活的方式来重写响应头中的值。示例如下:
application.yml
1 | spring: |
示例中,一个头的值为 /42?user=ford&password=omg!what&flag=true,在发出下游请求后,将此头设置为 /42?user=ford&password=***&flag=true。注意使用 $\ 代表 $,这是 YAML 格式指定的。
SaveSession
SaveSession 保存 Session,在向下游服务转发请求之前强制执行 WebSession::save操作
当使用一些类似于 Spring Session 的数据与惰性数据(lazy data)存储一起使用时,这特别有用,需要确保在进行转发请求之前已保存会话状态。示例如下:
application.yml
1 | spring: |
如果将 Spring Security 与 Spring Session 集成,并希望确保安全性详细信息已转发到远程进程,那么这一点至关重要。
SecureHeaders
SecureHeaders会向响应添加多个头数据,主要是根据这篇博客的建议:Everything you need to know about HTTP security headers。
会添加下面这些头,括号中是默认值。
- X-Xss-Protection:1 (mode=block)
- Strict-Transport-Security (max-age=631138519)
- X-Frame-Options (DENY)
- `X-Content-Type-Options (nosniff)
- Referrer-Policy (no-referrer)
- Content-Security-Policy (default-src ‘self’ https:; font-src ‘self’ https: data:; img-src ‘self’ https: data:; object-src ‘none’; script-src https:; style-src ‘self’ https: ‘unsafe-inline’)
- X-Download-Options (noopen)
- X-Permitted-Cross-Domain-Policies (none)
若要改变这些默认值,在 spring.cloud.gateway.filter.secure-headers命名空间中设置适当的值。下面这些属性可设置:
- xss-protection-header
- strict-transport-security
- x-frame-options
- x-content-type-options
- referrer-policy
- content-security-policy
- x-download-options
- x-permitted-cross-domain-policies
这些属性是与上面列出要添加的头是对应的,在 SecureHeadersProperties.java 安全头属性类文件中可以看到。
要禁用默认值,设置spring.cloud.gateway.filter.secure-headers.disable属性,值使用逗号分隔符(,)。注意,值必须是安全标头(secure headers)的小写全名。如下所示:
1 | spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security |
SetPath
SetPath设置路径,使用一个路径template 参数接收。它提供了一种简单的方法,通过允许路径的模板片段来操作请求路径。这使用了 Spring 框架中的 URI 模板,允许多个匹配片段。如下示例:
application.yml
1 | spring: |
此示例,对于 /red/blue 的请求路径,在发给下游服务之前将路径设置为 /blue。
SetRequestHeader
SetRequestHeader重置请求头的值,使用 name 和 value 参数接收值。如下所示:
1 | spring: |
GatewayFilter 给指定名称的请求头(name)替换(而不是添加)其值。示例中,如果下游服务器响应X-Request-Red:1234,则将其值替换为X-Request-Red:Blue,这是其它下游服务将收到的内容。
SetRequestHeader 知道用于匹配路径(path)或主机(host)的 URI变量。URI变量可以在要值中使用,并在运行时展开。如下示例:
application.yml.
1 | spring: |
SetResponseHeader
SetResponseHeader重置响应头中的值,使用 name 和 value参数接收值。如下示例:
application.yml
1 | spring: |
GatewayFilter 会替换所有指定请求头名称的值(而不是添加)。示例,如果下游服务响应头是X-Response-Red:1234,Gateway 客户端会收到被替为 X-Response-Red:Blue的响应头。
SetResponseHeader知道用于匹配路径(path)和主机(host)的 URI变量,变量可以在值中使用,并在运行时展开。示例如下:
1 | spring: |
SetStatus
SetStatus 设置响应头的 HTTP 编码,使用单个参数 status 接收。值必须是一个有效的 HttpStatus,它可能是个整数 404 或 枚举字符串形式表示的:NOT_FOUND。如下示例:
application.yml
1 | spring: |
此示例,设置响应的 HTTP status 为 401。
可以配置 SetStatus GatewayFilter,以在响应的头中从代理请求返回原始 HTTP 状态代码。如果配置了以下属性,则会将头添加到响应中:
application.yml
1 | spring: |
StripPrefix
StripPrefix 剥离前缀片段,使用一个参数 parts 接收。parts 参数声明路径(path)中有几部分要从请求中剥离,在发送到下游服务之前。如下示例:
application.yml
1 | spring: |
此示例,当一个请求通过网关访问 /name/blue/red,对 nameservice 的请求看起来像 nameservice/red,剥离了前缀 2 部分。
Retry
Retry重试,支持下面这些参数:
retries:重试次数
status:重试的 HTTP 状态码,使用
org.springframework.http.HttpStatus。method:重试使用的 HTTP 请求方式,使用
org.springframework.http.HttpMethod。series:重试的一系列状态码,使用
org.springframework.http.HttpStatus.Series。exceptions:那些异常需要重试。
backoff:为重试配置的间隔指数。重试在
firstBackoff *(factor ^ n)的间隔后执行,其中 n 是重试次数,是累加的。如果配置了
maxBackoff,则将应用的最大重试间隔限制为maxBackoff。如果basedOnPreviousValue为true,则使用prevBackoff * factor计算回退量。示例,如果第 1 次重试是间隔 2 秒,则第 2 次重试与第 1 次重试间隔 4 秒,第 3 次重试与 第 2 次重试间隔 8 秒。
如果启用 Retry 过滤器,下面是此过滤器的默认值:
- retries:3 次
- series:5XX 系列
- methods:GET 方式
- exceptions:IOException 和 TimeoutException
- backoff:禁用
以下是 Retry GatewayFilter 配置示例:
1 | spring: |
注意:当将重试过滤器与带有forward:前缀的 URL 一起使用时,应仔细编写目标端点,以便在发生错误的情况下,它不会做任何可能导致响应发送到客户端并提交的操作。 例如,如果目标端点是带注释的控制器,则目标控制器方法不应返回带有错误状态代码的 ResponseEntity。 相反,它应该引发 Exception 或发出错误信号(例如,通过Mono.error(ex)返回值),可以配置重试过滤器来进行重试处理。
**警告:**当将重试过滤器与任何带有 body 的 HTTP方法一起使用时,body 将被缓存,并且网关将受到内存的限制。 body 将缓存在 ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR定义的请求属性中,对象的类型是org.springframework.core.io.buffer.DataBuffer。
RequestSize
当请求大小大于限制时,RequestSize 网关过滤器工厂可以限制请求到达下游服务。
过滤器使用maxSize参数。maxSize 是一个 DataSize类型,因此值(value)可以定义为数字,后跟可选的DataUnit 后缀,例如 KB 或 MB。默认是 B代表字节。它是请求的允许大小限制,以字节为单位。配置如下所示:
application.yml
1 | spring: |
当请求因大小而被拒绝时,RequestSize GatewayFilter 工厂将响应状态设置为 413 Payload Too Large,并带有一个附加报头 errorMessage。 以下示例这样的 errorMessage:
1 | errorMessage` : `Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB |
注意:如果未在路由定义中作为筛选器参数提供,则默认请求大小设置为 5 MB。
ModifyRequestBody
ModifyRequestBody修改请求体,在请求被网关发给下游服务之前。只能使用 Java DSL 来配置此过滤器。如下示例:
1 |
|
ModifyResponseBody
ModifyResponseBody修改响应体,在返回响应给客户端之前。只能使用 Java DSL 来配置此过滤器。如下示例:
1 |
|
defaultFilters
添加一个过滤器来应用于所有的路由,可以使用spring.cloud.gateway.default-filters。该属性使用一个过滤器列表(list)。下面示例设置一个默认过滤器集合(set):
1 | spring: |
全局过滤器
GlobalFilter 接口与 GatewayFilterFactory 系列过滤器有着相同的签名。 实现 GlobalFilter接口的过滤器是特殊过滤器,有条件地应用于所有路由。
所有全局过滤器都实现了 GlobalFilter 和 Ordered,重写了 getOrder() 和 filter(ServerWebExchange exchange, GatewayFilterChain chain)方法。
**注意:**此接口及其用法可能会在将来的里程碑版本中更改。
组合全局和网关过滤器排序
当请求与路由匹配时,过滤 Web 处理器会将 GlobalFilter 的所有实例和特定于路由的 GatewayFilterFactory 实例添加到过滤器链中。该组合的过滤器链会通过 org.springframework.core.Ordered接口排序,可以通过实现getOrder()方法进行设置。
Spring Cloud Gateway 会区分过滤器逻辑执行的 pre(前置) 和 post(后置) 阶段,最高优先级的过滤器在 pre(前置)阶段是第一个执行,在post阶段是最后执行。查看 How it Works。
下面示例配置一个过滤器链:
1 |
|
ForwardRoutingFilter
ForwardRoutingFilter转发路由过滤器:会在 Exchange 属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 中查找 RUI。如果 URI 是 forward 模式(例如,forward:///localendpoint),它使用 Spring DispatcherHandler 来处理请求。请求 URI 中的 path 部分会被转发(forward) URL 中的 path 重写。
未修改的原始URL会附加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性中的列表中。
ForwardPathFilter
ForwardPathFilter从路由在获取 path,替换请求中的 path。
LoadBalancerClientFilter
LoadBalancerClientFilter负载均衡过滤器(已被标记弃用,使用 ReactiveLoadBalancerClientFilter)。
会在 Exchange 属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR中查找 URI。如果 URI 是 lb 模式(例如,lb://myservice),它会使用 Spring Cloud LoadBalancerClient 来将名称(示例是 myservice)解析成真实的 host 和 port,并替换请求的 URI。
未修改的原始 URL会附加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性列表中。过滤器也会在 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 属性列表中查找 URI 前缀是否等于lb ,如果是,则应用负载均衡规则。如下所示:
application.yml
1 | spring: |
**注意:**默认情况下,如果负载均衡器 LoadBalancer 找不到服务实例,则返回 503状态码。可以通过 设置spring.cloud.gateway.loadbalancer.use404=true 来配置网关返回 404。
**注意:**从负载均衡器 (LoadBalancer) 返回的服务实例(ServiceInstance)的 isSecure 的值将覆盖对网关的请求中指定的模式(scheme)。
例如,进入网关的请求是 HTTPS,但服务实例声明并不是安全的,下游服务通过 HTTP访问。相反的情况也可以适用。然后,如果在 Gateway 配置中指定了路由的 GATEWAY_SCHEME_PREFIX_ATTR ,前缀将被删除,被删除后的路由 URL 将覆盖服务实例中的配置。
警告:LoadBalancerClientFilter 的低庋使用阻塞(blocking)的 Ribbon LoadBalancerClient。 建议您改用ReactiveLoadBalancerClientFilter。 可以通过将spring.cloud.loadbalancer.ribbon.enabled的值设置为false来切换到它。
ReactiveLoadBalancerClientFilter
LoadBalancerClientFilter响应式负载均衡过滤器:会查找 Exchange 中名为 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的属性。如果 URL 是 lb 模式(例如,lb://myservice),它使用 Spring Cloud ReactorLoadBalancer 来把服务名(示例中是 myservice)解析为真实的主机(host)和端口(port),并替换同一属性的 URI。未修改的原始 URL 被附加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 属性列表中。
过滤器还会在 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 属性中查找其是否等于 lb。如果题,则应用相同的规则。配置示例如下:
application.yml
1 | spring: |
**注意:**默认情况下,如果负载均衡器 ReactorLoadBalancer 找不到服务实例(ServiceInstance),则返回 503状态码。可以通过主,以置spring.cloud.gateway.loadbalancer.use404=true 来配置网关返回 404。
**注意:**从负载均衡器 (ReactiveLoadBalancerClientFilter ) 返回的服务实例(ServiceInstance)的 isSecure 的值将覆盖对网关的请求中指定的模式(scheme)。
例如,进入网关的请求是 HTTPS,但服务实例声明并不是安全的,下游服务通过 HTTP访问。相反的情况也可以适用。然后,如果在 Gateway 配置中指定了路由的 GATEWAY_SCHEME_PREFIX_ATTR ,前缀将被删除,被删除后的路由 URL 将覆盖服务实例中的配置。
NoLoadBalancerClientFilter
NoLoadBalancerClientFilter是 GatewayNoLoadBalancerClientAutoConfiguration 的一个内部静态类,被自动注册为 Bean。
处理非负载均衡模式的请求。会从 ServerWebExchange 的属性列表中获取 GATEWAY_REQUEST_URL_ATTR 和 GATEWAY_SCHEME_PREFIX_ATTR 的值,判断 URL 的 Scheme 和 前缀是否等于 lb,如果都不相等,非负载均衡请求,则继续往下走。
Netty Routing Filter
NettyRoutingFilterHTTP或HTTPS请求协议路由过滤器: 如果位于 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR Exchange 属性中的 URL 是 http 或 https 方案,则将运行 Netty 路由过滤器。 它使用 Netty HttpClient 代理转发到下游的请求。
响应被放入ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR Exchange 属性中,以供后续的过滤器使用。 (还有一个实验性的 WebClientHttpRoutingFilter 执行相同的功能,但不需要 Netty)。
Netty Write Response Filter
NettyWriteResponseFilter重写 Netty HTTP 响应过滤器: 如果 Exchange 的 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR属性中存在Netty HttpClientResponse,则NettyWriteResponseFilter 将运行。
它在所有其他过滤器完成后运行,并将代理响应写回到网关客户端响应。 (还有一个实验性的 WebClientWriteResponseFilter 执行相同的功能,但不需要 Netty)。
RouteToRequestUrl Filter
RouteToRequestUrlFilter将请求的 URL 替换为 Route 中的 URL:如果 Exchange 的ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR Exchange 属性中有一个Route对象,则RouteToRequestUrlFilter 将运行。
它基于请求 URI 创建一个 新URI,但使用 Route 对象的 URI 属性进行更新。 新的URI 放置在ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR Exchange 属性中。
如果 URI 具有前缀模式(例如 lb:ws://serviceid),则将从 URI 中剥离 lb,并将其放置在ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR中,以供后续的过滤器链使用。
Websocket Routing Filter
WebsocketRoutingFilter Websocket 路由过滤器:如果位于ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR Exchange 属性中的 URL 具有 ws 或 wss 模式,则将运行 websocket 路由过滤器。 它使用 Spring WebSocket 基础结构向下游转发 websocket 请求。
还可以通过 URI 前缀 lb 对 websockets 进行负载均衡,例如,lb:ws//serviceid。
**注意:**如果使用 SockJS 作为普通 HTTP 的回退,则应配置普通HTTP 路由 和 websocket 路由。
下面是 websocket 路由过滤器的配置示例:
application.yml
1 | spring: |
Gateway Metrics Filter
GatewayMetricsFilter 网关监控/指标过滤器:要启用网关 监控/指标,添加spring-boot-starter-actuator依赖。然后,默认情况下,只要属性spring.cloud.gateway.metrics.enabled未设置为 false,网关 监控/指标 过滤器就会运行。
此过滤器添加名为 gateway.requests 的计时器监控/指标,其标记如下:
- routeId: 路由 ID
- routeUri: 路由到 API 的 URI
- outcome: 效果, 按 HttpStatus.Series 分类
- status: 返回给客户端的请求的HTTP状态(HTTP status)
- httpStatusCode: 返回给客户端的请求的HTTP状态(HTTP Status)
- httpMethod: HTTP 请求方式
然后,可以从 /actuator/metrics/gateway.requests 中获取这些监控/指标,并且可以很容易地与Prometheus集成以创建 Grafana仪表板。
**注意:**要启用 Prometheus 端点,需要引入 micrometer-registry-prometheus 依赖。
RemoveCachedBodyFilter
RemoveCachedBodyFilter移除保存在 ServerWebExchange 属性列表中的 请求缓存体。
Marking An Exchange As Routed
标记 Exchange 为已路由:网关已经路由ServerWebExchange之后,通过将gatewayAlreadyRouted添加到 Exchange 属性列表中来将 Exchange 标记为 已路由(routed),其他路由过滤器将不会再次路由请求,实质上会跳过路由过滤器。
可以使用多种便捷方法将交换标记为已路由,或者检查交换是否已路由。
ServerWebExchangeUtils.isAlreadyRouted接收一个ServerWebExchange对象,并检测它是否已被路由(routed)。ServerWebExchangeUtils.setAlreadyRouted接收一个ServerWebExchange对象,并将其标记为已路由(routed)。
HTTP头过滤器
HttpHeadersFilter 在向下游服务发送请求之前应用于请求,例如在 NettyRoutingFilter中。
实现 HttpHeadersFilter 和 Ordered 的过滤器重写 getOrder() 和 filter(HttpHeaders input, ServerWebExchange exchange) 方法。
ForwardedHeadersFilter
ForwardedHeadersFilter(转发)头过滤器创建一个 Forwarded头以发送到下游服务。 它将当前请求的host头,scheme和port添加到任何现有的Forward头中。
RemoveHopByHopHeadersFilter
RemoveHopByHopHeadersFilter头过滤器移除转发(forwarded)请求的头。被删除的头的默认列表来自 IETF。
配置属性:spring.cloud.gateway.filter.remove-hop-by-hop
默认移除的头有:
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- TE
- Trailer
- Transfer-Encoding
- Upgrade
要更改此设置,将spring.cloud.gateway.filter.remove-non-proxy-headers.headers属性设置为要删除的标头名称列表。
XForwardedHeadersFilter
XForwardedHeadersFilter头过滤器创建各种 X-Forwarded-* 头来发给下游服务。使用当前请求的host头,scheme, port, path来创建。配置属性:spring.cloud.gateway.x-forwarded。
可以通过以下布尔属性(默认为 true)控制单个头的创建:
- spring.cloud.gateway.x-forwarded.for.enabled
- spring.cloud.gateway.x-forwarded.host.enabled
- spring.cloud.gateway.x-forwarded.port.enabled
- spring.cloud.gateway.x-forwarded.proto.enabled
- spring.cloud.gateway.x-forwarded.prefix.enabled
可以通过以下布尔属性(默认为 true)控制追加多个标头:
- spring.cloud.gateway.x-forwarded.for.append
- spring.cloud.gateway.x-forwarded.host.append
- spring.cloud.gateway.x-forwarded.port.append
- spring.cloud.gateway.x-forwarded.proto.append
- spring.cloud.gateway.x-forwarded.prefix.append
Spring Cloud(二十):Gateway 路由匹配表达式工厂,过滤器工厂,全局过滤器,HTTP头过滤器
http://blog.gxitsky.com/2020/03/13/SpringCloud-20-gateway-route-filter-global-headers/

