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 | 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
0.0.0.1, 0.0.0.2, 0.0.0.3 :
maxTrustedIndex
值将使用对应的远程地址:maxTrustedIndex result [ Integer.MIN_VALUE
,0](invalid, IllegalArgumentException
during 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 | 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 | 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 | `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
NettyRoutingFilter
HTTP或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/