本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
4.0.2-SNAPSHOT
这个项目提供了一个建立在Spring生态系统之上的API网关,包括。Spring 6、Spring Boot 3 和Project Reactor。Spring Cloud Gateway旨在提供一种简单而有效的方式来路由到API,并为其提供跨领域的关注,如:安全、监控/指标和容错。
1. 如何添加 Spring Cloud Gateway
要在你的项目中添加 Spring Cloud Gateway,请使用 group ID 为 org.springframework.cloud
,artifact ID 为 spring-cloud-starter-gateway
的starter。请参阅 Spring Cloud项目页面,了解使用当前 Spring Cloud Release Train 设置构建系统的详情。
如果你添加了 starter,但你不希望启用网关,请设置 spring.cloud.gateway.enabled=false
。
Spring Cloud Gateway 是建立在 Spring Boot 2.x、 Spring WebFlux 和 Project Reactor之上。因此,你所熟悉的许多同步库(例如Spring Data和Spring Security)和模式在你使用Spring Cloud Gateway时可能不适用。如果你不熟悉这些项目,我们建议你在使用Spring Cloud Gateway之前,先阅读它们的文档,熟悉一些新概念。 |
Spring Cloud Gateway需要Spring Boot和Spring Webflux提供的Netty运行时。它不能在传统的Servlet容器中工作,也不能以WAR的形式构建。 |
2. 术语表
-
Route(路由): 网关的基本构件。它由一个ID、一个目的地URI、一个谓词(Predicate)集合和一个过滤器(Filter)集合定义。如果集合谓词为真,则路由被匹配。
-
Predicate(谓词): 这是一个 Java 8 Function Predicate。输入类型是 Spring Framework
ServerWebExchange
。这让你可以在HTTP请求中的任何内容上进行匹配,比如header或查询参数。 -
Filter(过滤器): 这些是
GatewayFilter
的实例,已经用特定工厂构建。在这里,你可以在发送下游请求之前或之后修改请求和响应。
3. 它是如何工作的
下图提供了一个关于 Spring Cloud Gateway 如何工作的高层次概述。
客户端向 Spring Cloud Gateway 发出请求。如果Gateway处理程序映射确定一个请求与路由相匹配,它将被发送到Gateway Web处理程序。这个处理程序通过一个特定于该请求的过滤器链来运行该请求。过滤器被虚线分割的原因是,过滤器可以在代理请求发送之前和之后运行逻辑。所有的 "pre"
(前)过滤器逻辑都被执行。然后发出代理请求。在代理请求发出后,"post"
(后)过滤器逻辑被运行。
在路由中定义的没有端口的URI,其HTTP和HTTPS URI的默认端口值分别为80和443。 |
4. 配置Route ,Predicate ,Filter 工厂
有两种方式来配置谓词和过滤器:快捷方式和完全展开的参数。下面的大多数例子都使用快捷方式。
名称和参数名称作为 code
列在每一节的第一或第二句中。参数通常按照快捷方式配置所需的顺序列出。
4.1. 快捷方式的配置
快捷方式配置由过滤器名称(filter name),后面跟一个等号 (=
),然后是用逗号 (,
) 分隔的参数值。
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- Cookie=mycookie,mycookievalue
上面的例子定义了 Cookie
路由谓词工厂,有两个参数,cookie 名称, mycookie
和与 mycookievalue
匹配的值。
4.2. 完全展开的参数
完全展开的参数看起来更像标准的yaml配置,有名称/值对。一般来说,会有一个 name
key和一个 args
key。args
key是一个键值对的映射,用于配置谓词或过滤器。
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue
这就是上面所示的 Cookie
谓词的快捷配置的完整配置。
5. Route Predicate(路由谓词)工厂
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping
基础设施的一部分。Spring Cloud Gateway包括许多内置的路由谓词工厂。所有这些谓词都与HTTP请求的不同属性相匹配。你可以用逻辑 and
语句组合多个路由谓词工厂。
5.1. After
After
路由谓词工厂需要一个参数,即一个日期时间(这是一个java ZonedDateTime
)。这个谓词匹配发生在指定日期时间之后的请求。下面的例子配置了一个After路由谓词。
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
该路由与北美山区时间(丹佛)2017年1月20日17:42之后发出的任何请求相匹配。
5.2. Before
Before
路由谓词工厂只需要一个参数,即 datetime
(这是一个java ZonedDateTime
)。这个谓词匹配发生在指定 datetime
之前的请求。下面的例子配置了一个Before路由谓词。
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
该路由与北美山区时间2017年1月20日17:42(丹佛)之前发出的任何请求相匹配。
5.3. Between
Between
路由谓词工厂需要两个参数,datetime1
和 datetime2
,它们是java ZonedDateTime
对象。这个谓词匹配发生在 datetime1
之后和 datetime2
之前的请求。datetime2
的参数必须在 datetime1
之后。下面的例子配置了一个 between 路由谓词。
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
这个路由匹配2017年1月20日山区时间(丹佛)17:42之后和2017年1月21日山区时间(丹佛)17:42之前的任何请求。这对维护窗口可能是有用的。
5.4. Cookie
Cookie
路由谓词工厂接受两个参数,即 cookie name
和一个 regexp
(这是一个Java正则表达式)。这个谓词匹配具有给定名称且其值符合正则表达式的cookie。下面的例子配置了一个cookie路由谓词工厂。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
这个路由匹配有一个名为 chocolate
的cookie,其值符合 ch.p
正则表达式的请求。
5.5. Header
Header
路由谓词工厂需要两个参数,header
和一个 regexp
(这是一个Java正则表达式)。这个谓词与具有给定名称且其值与正则表达式相匹配的 header 匹配。下面的例子配置了一个 header 路由谓词。
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
如果请求有一个名为 X-Request-Id
的header,其值与 \d+
正则表达式相匹配(也就是说,它的值是一个或多个数字),则该路由匹配。
5.6. Host
Host
路由谓语工厂接受一个参数:一个主机(Host)名称的 patterns
列表。该pattern是Ant风格的模式,以 .
为分隔符。这个谓词匹配符合该pattern的Host header。下面的例子配置了一个 host 路由谓词。
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
支持URI模板变量(如 {sub}.myhost.org
)。
如果请求的 Host
header的值为 www.somehost.org
或 beta.somehost.org
或 www.anotherhost.org
,则该路由匹配。
这个谓词提取URI模板变量(比如前面例子中定义的 sub
)作为名称和值的映射,并将其放在 ServerWebExchange.getAttributes()
中,key值定义在 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
。然后这些值就可以被 GatewayFilter
工厂使用了。
5.7. Method
Method
路由谓词工厂接受一个 methods
参数,它是一个或多个参数:要匹配的HTTP方法。下面的例子配置了一个 method 路由谓词。
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
如果请求方式是 GET
或 POST
,则该路由匹配。
5.8. Path
Path
路由谓词工厂需要两个参数:一个Spring PathMatcher
patterns
的list和一个可选的flag matchTrailingSlash
(默认为 true
)。下面的例子配置了一个path路由谓词。
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
如果请求路径是 /red/1
或 /red/1/
或 /red/blue
或 /blue/green
,则该路由匹配。
如果 matchTrailingSlash
被设置为 false
,那么请求路径 /red/1/
将不会被匹配。
这个谓词提取URI模板变量(比如前面例子中定义的 segment
)作为name和value的映射,并把它放在 ServerWebExchange.getAttributes()
中,KEY值定义在 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
。然后这些值就可以被 GatewayFilter
工厂使用了。
有一个实用的方法(称为 get
),可以使访问这些变量变得更容易。下面的例子显示了如何使用 get
方法。
Map<String, String> uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange);
String segment = uriVariables.get("segment");
5.9. Query
Query
路由谓词工厂需要两个参数:一个必需的 param
和一个可选的 regexp
(这是一个Java正则表达式)。下面的例子配置了一个 query 路由谓词。
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
如果请求包含一个 green
的查询参数,前面的路由就会匹配。
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
如果请求中包含一个 red
的查询参数,其值与 gree.
表达式相匹配,那么路由就会匹配。例如: green
和 greet
。
5.10. RemoteAddr
RemoteAddr
路由谓词工厂接受一个 sources
集合(最小长度为1),它是CIDR注解(IPv4或IPv6)字符串,如 192.168.0.1/16
(其中 192.168.0.1
是一个IP地址,16
是一个子网掩码)。下面的例子配置了一个RemoteAddr路由谓词。
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
如果请求的远程地址 192.168.1.10
,则该路由匹配。
5.10.1. 修改远程地址(Remote Addresse)的解析方式
默认情况下,RemoteAddr路由谓语工厂使用传入请求中的远程地址。如果Spring Cloud Gateway位于代理层后面,这可能与实际的客户IP地址不一致。
你可以通过设置一个自定义的 RemoteAddressResolver
来定制远程地址的解析方式。Spring Cloud Gateway有一个非默认的远程地址解析器,它是基于 X-Forwarded-For Header的,即 XForwardedRemoteAddressResolver
。
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个受信任的基础设施,那么应该使用2的值。
请考虑以下 header 值。
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3
以下的 maxTrustedIndex
值产生以下的远程地址。
maxTrustedIndex |
结果 |
---|---|
[ |
(无效, 初始化时会抛出 |
1 |
0.0.0.3 |
2 |
0.0.0.2 |
3 |
0.0.0.1 |
[4, |
0.0.0.1 |
下面的例子显示了如何用Java实现同样的配置。
RemoteAddressResolver 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")
)
5.11. Weight
Weight
路由谓语工厂需要两个参数:group
和 weight
(一个int值)。weight 是按 group 计算的。下面的例子配置了一个 weight 路由谓词。
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
此路由将转发~80%的流量到 weighthigh.org
,~20%的流量到 weighlow.org
。
5.12. XForwarded Remote Addr
XForwarded Remote Addr
路由谓语工厂接受一个 sources
集合(最长度为 1),这些 sources 是 CIDR注解(IPv4 或 IPv6)字符串,如 192.168.0.1/16
(其中 192.168.0.1
是一个 IP 地址,16
是子网掩码)。
这个路由谓词允许根据 X-Forwarded-For
的 HTTP Header 对请求进行过滤。
这可以与反向代理一起使用,如负载均衡器或web应用防火墙,只有当请求来自这些反向代理所使用的受信任的IP地址列表时,才应该被允许。
下面的例子配置了一个 XForwardedRemoteAddr
路由谓词。
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- XForwardedRemoteAddr=192.168.1.1/24
例如,如果 X-Forwarded-For
Header 包含 192.168.1.10
,则该路由匹配。
6. GatewayFilter
工厂
路由(Route)过滤器(Filter)允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。路由过滤器的范围是一个特定的路由。Spring Cloud Gateway 包括许多内置的 GatewayFilter
工厂。
关于如何使用以下任何过滤器的更详细的例子,请看 单元测试。 |
6.1. AddRequestHeader
AddRequestHeader
GatewayFilter
工厂需要一个 name
和 value
参数。下面的例子配置了一个 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
header添加到所有匹配请求的下游请求的header信息中。
AddRequestHeader
知道用于匹配路径或主机的URI变量。URI变量可以在值中使用,并在运行时被扩展。下面的例子配置了一个 AddRequestHeader
GatewayFilter
,它使用一个变量。
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- AddRequestHeader=X-Request-Red, Blue-{segment}
6.2. AddRequestHeadersIfNotPresent
AddRequestHeadersIfNotPresent
GatewayFilter
工厂接受一个由冒号分隔的 name
和 value
键值对的集合。下面的例子配置了一个 AddRequestHeadersIfNotPresent
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: add_request_headers_route
uri: https://example.org
filters:
- AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-2:green
这个列表为所有匹配的请求在下游请求的header信息中添加了两个header信息 X-Request-Color-1:blue
和 X-Request-Color-2:green
。这类似于 AddRequestHeader
的工作方式,但与 AddRequestHeader
不同的是,它只在header 信息不存在的情况下才会这样做。否则,客户端请求中的原始值将被发送。
此外,要设置一个多值header,可以多次使用header的名称,如 AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-1:green
。
AddRequestHeadersIfNotPresent
也支持URI变量,用于匹配路径或主机。URI变量可以在值中使用,并在运行时被扩展。下面的例子配置了一个使用变量的 AddRequestHeadersIfNotPresent
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- AddRequestHeadersIfNotPresent=X-Request-Red:Blue-{segment}
6.3. AddRequestParameter
AddRequestParameter
GatewayFilter
Factory需要一个 name
和 value
参数。下面的例子配置了一个 AddRequestParameter
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=red, blue
这将为所有匹配的请求在下游请求的查询字符串中添加 red=blue
。
AddRequestParameter
知道用于匹配路径或主机的URI变量。URI变量可以在值中使用,并在运行时被扩展。下面的例子配置了一个 AddRequestParameter
GatewayFilter
,它使用了一个变量。
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddRequestParameter=foo, bar-{segment}
6.4. AddResponseHeader
AddResponseHeader
GatewayFilter
工厂需要一个 name
和 value
参数。下面的例子配置了一个 AddResponseHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Red, Blue
这将把 X-Response-Red:Blue
header添加到所有匹配请求的下游响应的header中。
AddResponseHeader
知道用于匹配路径或主机的URI变量。URI变量可以在值中使用,并在运行时被扩展。下面的例子配置了一个 AddResponseHeader
GatewayFilter
,它使用了一个变量。
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddResponseHeader=foo, bar-{segment}
6.5. CircuitBreaker
Spring Cloud CircuitBreaker GatewayFilter 工厂使用Spring Cloud CircuitBreaker API 将 Gateway 路由包裹在一个熔断器中。Spring Cloud CircuitBreaker 支持多个可与 Spring Cloud Gateway 一起使用的库。Spring Cloud 支持 Resilience4J 开箱即用。
要启用 Spring Cloud CircuitBreaker 过滤器,你需要添加 spring-cloud-starter-circuitbreaker-reactor-resilience4j
依赖。下面的例子配置了一个 Spring Cloud CircuitBreaker GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: circuitbreaker_route
uri: https://example.org
filters:
- CircuitBreaker=myCircuitBreaker
要配置熔断器,请参阅你所使用的底层熔断器实现的配置。
Spring Cloud CircuitBreaker 过滤器还可以接受一个可选的 fallbackUri
参数。目前,只支持 forward:
模式的URI。如果fallback被调用,请求将被转发到URI所匹配的控制器。下面的例子配置了这样一个fallback。
spring:
cloud:
gateway:
routes:
- id: circuitbreaker_route
uri: lb://backing-service:8088
predicates:
- Path=/consumingServiceEndpoint
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/inCaseOfFailureUseThis
- RewritePath=/consumingServiceEndpoint, /backingServiceEndpoint
也可以通过Java来实现相同的配置,如下。
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint")
.filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis"))
.rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088")
.build();
}
当熔断器 fallback 被调用时,这个例子转发到 /inCaseofFailureUseThis
URI。请注意,这个例子还演示了(可选)Spring Cloud LoadBalancer 的负载均衡(由目标URI上的 lb
前缀定义)。
CircuitBreaker 还支持 fallbackUri
中的URI变量。这允许更复杂的路由选项,比如使用 PathPattern 表达式 转发原始主机或URL路径的部分。
在下面的例子中,调用 consumingServiceEndpoint/users/1
将被重定向到 inCaseOfFailureUseThis/users/1
。
spring:
cloud:
gateway:
routes:
- id: circuitbreaker_route
uri: lb://backing-service:8088
predicates:
- Path=/consumingServiceEndpoint/{*segments}
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/inCaseOfFailureUseThis/{segments}
一般情况下是使用 fallbackUri
来定义网关应用程序中的内部controller或handler。然而,你也可以将请求重新路由到外部应用程序的controller或handler,如下所示。
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: CircuitBreaker
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
在这个例子中,网关应用程序中没有 fallback
端点或处理程序。然而,在另一个应用程序中有一个,在 localhost:9994
下注册。
在请求被转发到 fallback 的情况下,Spring Cloud CircuitBreaker Gateway 过滤器也提供了造成这种情况的 Throwable
。它作为 ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR
属性被添加到 ServerWebExchange
中,在网关应用中处理 fallback 时可以使用。
对于外部 controller/handler 的情况,可以添加带有异常细节的header。你可以在FallbackHeaders GatewayFilter Factory 部分找到更多关于这样做的信息。
6.5.1. 熔断指定的状态码
在某些情况下,你可能想根据它所包裹的路由返回的状态码来熔断。断路器配置对象需要一个状态码列表,如果返回这些代码将导致断路器熔断。当设置你想让断路器熔断的状态代码时,你可以使用一个带有状态码值的 int 或 HttpStatus
枚举的字符串表示。
spring:
cloud:
gateway:
routes:
- id: circuitbreaker_route
uri: lb://backing-service:8088
predicates:
- Path=/consumingServiceEndpoint
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/inCaseOfFailureUseThis
statusCodes:
- 500
- "NOT_FOUND"
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint")
.filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis").addStatusCode("INTERNAL_SERVER_ERROR"))
.rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088")
.build();
}
6.6. CacheRequestBody
有些情况下,有必要读取请求体。由于请求体只能被读取一次,我们需要缓存请求体。你可以使用 CacheRequestBody
过滤器来缓存请求体,然后再把它发送到下游,从 exchange
属性中获取请求体。
下面显示了如何缓存请求体 GatewayFilter
:
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("cache_request_body_route", r -> r.path("/downstream/**")
.filters(f -> f.prefixPath("/httpbin")
.cacheRequestBody(String.class).uri(uri))
.build();
}
spring:
cloud:
gateway:
routes:
- id: cache_request_body_route
uri: lb://downstream
predicates:
- Path=/downstream/**
filters:
- name: CacheRequestBody
args:
bodyClass: java.lang.String
CacheRequestBody
提取请求体并将其转换为一个 body 类(比如前面例子中定义的 java.lang.String
)。然后,CacheRequestBody
把它放在 ServerWebExchange.getAttributes()
提供的属性中,其KEY值在 ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR
中定义。
这个过滤器只对HTTP(包括HTTPS)请求起作用。 |
6.7. DedupeResponseHeader
DedupeResponseHeader
GatewayFilter
工厂接受一个 name
参数和一个可选的 strategy
参数。name
可以包含一个以空格分隔的header名称列表。下面的例子配置了一个 DedupeResponseHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: dedupe_response_header_route
uri: https://example.org
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
在网关CORS逻辑和下游逻辑都添加了 Access-Control-Allow-Credentials
和 Access-Control-Allow-Origin
响应头的情况下,这将删除重复的值。
DedupeResponseHeader
过滤器还接受一个可选的 strategy
参数。接受的值是 RETAIN_FIRST
(默认)、RETAIN_LAST
和 RETAIN_UNIQUE
。
6.8. FallbackHeaders
通过 FallbackHeaders
工厂,你可以在转发到外部应用程序中的 fallbackUri
的请求的header中添加Spring Cloud CircuitBreaker的执行异常细节,如以下场景。
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: CircuitBreaker
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
filters:
- name: FallbackHeaders
args:
executionExceptionTypeHeaderName: Test-Header
在这个例子中,在运行断路器时发生执行异常后,请求被转发到运行在 localhost:9994
的应用程序中的 fallback
端点或 handler。带有异常类型、消息和(如果有)根本原因的异常类型和消息的 header 被 FallbackHeaders
过滤器添加到该请求中。
你可以通过设置以下参数的值(显示为默认值)来覆盖配置中header的名称。
-
executionExceptionTypeHeaderName
("Execution-Exception-Type"
) -
executionExceptionMessageHeaderName
("Execution-Exception-Message"
) -
rootCauseExceptionTypeHeaderName
("Root-Cause-Exception-Type"
) -
rootCauseExceptionMessageHeaderName
("Root-Cause-Exception-Message"
)
关于断路器和网关的更多信息,请参见 Spring Cloud CircuitBreaker Factory 部分 。
6.9. JsonToGrpc
JSONToGRPCFilter
GatewayFilter
Factory 将一个JSON payload 转换为gRPC请求。
该过滤器需要以下参数。
-
protoDescriptor
: Proto描述文件。
这个文件可以用 protoc
生成,并指定 --descriptor_set_out
标志。
protoc --proto_path=src/main/resources/proto/ \
--descriptor_set_out=src/main/resources/proto/hello.pb \
src/main/resources/proto/hello.proto
-
protoFile
: Proto定义文件。 -
service
: 处理请求的服务的全名称。 -
method
: 处理该请求的服务中的方法名称。
支持 streaming 。
|
application.yml.
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("json-grpc", r -> r.path("/json/hello").filters(f -> {
String protoDescriptor = "file:src/main/proto/hello.pb";
String protoFile = "file:src/main/proto/hello.proto";
String service = "HelloService";
String method = "hello";
return f.jsonToGRPC(protoDescriptor, protoFile, service, method);
}).uri(uri))
spring:
cloud:
gateway:
routes:
- id: json-grpc
uri: https://localhost:6565/testhello
predicates:
- Path=/json/**
filters:
- name: JsonToGrpc
args:
protoDescriptor: file:proto/hello.pb
protoFile: file:proto/hello.proto
service: com.example.grpcserver.hello.HelloService
method: hello
当通过网关向 /json/hello
发出请求时,该请求通过使用 hello.proto
中提供的定义进行转换,发送到 com.example.grpcserver.hello.HelloService/hello
,返回的响应被转换为JSON。
默认情况下,它通过使用默认的 TrustManagerFactory
创建一个 NettyChannel
。然而,你可以通过创建一个 GrpcSslConfigurer
类型的bean来定制这个 TrustManager
。
@Configuration
public class GRPCLocalConfiguration {
@Bean
public GRPCSSLContext sslContext() {
TrustManager trustManager = trustAllCerts();
return new GRPCSSLContext(trustManager);
}
}
6.10. LocalResponseCache
这个过滤器允许缓存响应体和header,遵循以下规则。
-
它只能缓存无请求体的GET请求。
-
它只对以下状态代码之一的响应进行缓存。HTTP 200(OK),HTTP 206(部分内容),或HTTP 301(永久移动)。
-
如果
Cache-Control
header不允许,响应数据就不会被缓存(请求中存在no-store
或响应中存在no-store
或private
)。 -
如果响应已经被缓存,并且在
Cache-Control
头中用no-cache
值执行一个新的请求,它将返回一个304(未修改)的无body的响应。
这个过滤器(配置每个路由的本地响应缓存)只有在启用了本地响应全局缓存的情况下才可用。
它接受第一个参数,用于覆盖缓存条目过期的时间(用 s
表示秒,用 m
表示分钟,用 h
表示小时),第二个参数用于设置该路由驱逐条目的最大缓存大小(KB、MB或GB)。
下面的列表显示了如何添加本地响应缓存 GatewayFilter
。
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
.filters(f -> f.prefixPath("/httpbin")
.localResponseCache(Duration.ofMinutes(30), "500MB")
).uri(uri))
.build();
}
或者,这样:
spring:
cloud:
gateway:
routes:
- id: resource
uri: http://localhost:9000
predicates:
- Path=/resource
filters:
- LocalResponseCache=30m,500MB
这个过滤器还自动计算 HTTP Cache-Control header中的 max-age 值。只有在原始响应中存在 max-age 的情况下,才会用 timeToLive 配置参数中设置的秒数重写该值。在连续的调用中,这个值会以响应过期前的剩余秒数重新计算。
|
6.11. MapRequestHeader
MapRequestHeader
GatewayFilter
工厂接受 fromHeader
和 toHeader
参数。它创建一个新的命名header(toHeader
),并从传入的http请求的现有命名头(fromHeader
)中提取值。如果输入的header不存在,过滤器没有任何影响。如果新的命名header信息已经存在,它的值就会被增加新的值。下面的例子配置了一个 MapRequestHeader
。
spring:
cloud:
gateway:
routes:
- id: map_request_header_route
uri: https://example.org
filters:
- MapRequestHeader=Blue, X-Request-Red
这将在下游请求中添加 X-Request-Red:<values>
头,并从传入的HTTP请求的 Blue
头中更新数值。
6.12. ModifyRequestBody
你可以使用 ModifyRequestBody
过滤器,在网关向下游发送请求体之前对其进行修改。
这个过滤器只能通过使用Java DSL来配置。 |
下面显示了如何使用 GatewayFilter
修改请求体:
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
.filters(f -> f.prefixPath("/httpbin")
.modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
(exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
.build();
}
static class Hello {
String message;
public Hello() { }
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
如果请求没有正文,RewriteFilter 将被传递为 null 。应该返回 Mono.empty() 来指定请求中缺少的主体。
|
6.13. ModifyResponseBody
你可以使用 ModifyResponseBody
过滤器来修改响应体,然后再把它送回给客户端。
这个过滤器只能通过使用Java DSL来配置。 |
下面显示了如何使用 GatewayFilter
修改响应体 。
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
.filters(f -> f.prefixPath("/httpbin")
.modifyResponseBody(String.class, String.class,
(exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri))
.build();
}
如果响应没有正文,RewriteFilter 将被传递为 null 。应该返回 Mono.empty() 来指定响应中缺少的主体。
|
6.14. PrefixPath
PrefixPath
GatewayFilter
工厂需要一个 prefix
参数。下面的例子配置了一个 PrefixPath
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
这就把 /mypath
作为所有匹配请求的路径的前缀。因此,一个到 /hello
的请求会被发送到 /mypath/hello
。
6.15. PreserveHostHeader
PreserveHostHeader
GatewayFilter
工厂没有参数。这个过滤器设置一个请求属性(request attribute),路由过滤器(routing filter)会检查该属性,以确定是否应该发送原始的主机头,而不是由HTTP客户端确定的主机头。下面的例子配置了一个 PreserveHostHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: https://example.org
filters:
- PreserveHostHeader
6.16. RedirectTo
RedirectTo
GatewayFilter
工厂需要两个参数,status
和 url
。status
参数应该是一个300系列的重定向 HTTP 状态码,如301。url
参数应该是一个有效的URL。这就是 Location
header 的值。对于相对重定向,你应该使用 uri: no://op
作为路由定义的uri。下面的列表配置了一个 RedirectTo
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- RedirectTo=302, https://acme.org
这将发送一个带有 Location:https://acme.org
header的302状态码的响应,以执行重定向。
6.17. RemoveJsonAttributesResponseBody
RemoveJsonAttributesResponseBody
GatewayFilter
工厂接收了一个要搜索的属性名称(attribute name
)集合,列表中的最后一个参数可以是一个布尔值,用来删除根级的属性(如果该参数未定义,那就是默认值 false
)或递归(true
)。它提供了一个方便的方法,通过删除属性来应用于JSON body内容的转换。
下面的例子配置了一个 RemoveJsonAttributesResponseBody
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: removejsonattributes_route
uri: https://example.org
filters:
- RemoveJsonAttributesResponseBody=id,color
这从根层的JSON body中删除了属性 "id" 和 "color"。
下面的例子配置了一个 RemoveJsonAttributesResponseBody
GatewayFilter
,它使用了可选的最后参数。
spring:
cloud:
gateway:
routes:
- id: removejsonattributes_recursively_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- RemoveJsonAttributesResponseBody=id,color,true
这将从任何级别的JSON body中移除属性 "id" 和 "color"。
6.18. RemoveRequestHeader
RemoveRequestHeader
GatewayFilter
工厂需要一个 name
参数。它是要被删除的header的名称。下面配置了一个 RemoveRequestHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
这在向下游发送之前删除了 X-Request-Foo
标头。
6.19. RemoveRequestParameter
RemoveRequestParameter
GatewayFilter
工厂需要一个 name
参数。它是要删除的查询参数的名称。下面的例子配置了一个 RemoveRequestParameter
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: removerequestparameter_route
uri: https://example.org
filters:
- RemoveRequestParameter=red
这将在向下游发送之前删除 red
参数。
6.20. RemoveResponseHeader
RemoveResponseHeader
GatewayFilter
工厂需要一个 name
参数。它是要被移除的 header 的名称。下面的列表配置了一个 RemoveResponseHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: https://example.org
filters:
- RemoveResponseHeader=X-Response-Foo
这将在响应返回到网关客户端之前从响应中删除 X-Response-Foo
头。
要删除任何类型的敏感标头,你应该为任何你可能想这样做的路由配置这个过滤器。此外,你可以通过使用 spring.cloud.gateway.default-filters
配置一次此过滤器,并将其应用于所有路由。
6.21. RequestHeaderSize
RequestHeaderSize
GatewayFilter
工厂接受 maxSize
和 errorHeaderName
参数。maxSize
参数是请求头(包括key和value)所允许的最大数据大小。errorHeaderName
参数设置包含错误信息的响应头的名称,默认为 "errorMessage"。下面的列表配置了一个 RequestHeaderSize
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: requestheadersize_route
uri: https://example.org
filters:
- RequestHeaderSize=1000B
如果任何请求头的大小超过1000字节,这将发送一个 431状态码的响应。
6.22. RequestRateLimiter
RequestRateLimiter
GatewayFilter
工厂使用 RateLimiter
实现来确定是否允许当前请求继续进行。如果不允许,就会返回 HTTP 429 - Too Many Requests
(默认)的状态。
这个过滤器需要一个可选的 keyResolver
参数和特定于速率限制器的参数(在本节后面描述)。
keyResolver
是一个实现了 KeyResolver
接口的Bean。在配置中,使用SpEL来引用Bean的名字。#{@myKeyResolver}
是一个SpEL表达式,它引用了一个名为 myKeyResolver
的bean。下面的列表显示了 KeyResolver
的接口。
public interface KeyResolver {
Mono<String> resolve(ServerWebExchange exchange);
}
KeyResolver
接口让可插拔的策略导出限制请求的key。在未来的里程碑版本中,会有一些 KeyResolver
的实现。
KeyResolver
的默认实现是 PrincipalNameKeyResolver
,它从 ServerWebExchange
中检索 Principal
并调用 Principal.getName()
。
默认情况下,如果 KeyResolver
没有找到一个 key
,请求会被拒绝。你可以通过设置 spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key
(true
或 false
)和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code
属性来调整这种行为。
Example 43. application.properties
# 无效的快捷方式配置 spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver} |
6.22.1. Redis RateLimiter
Redis的实现是基于 Stripe 的工作。它需要使用 spring-boot-starter-data-redis-reactive
Spring Boot Starter。
使用的算法是 令牌桶算法。
redis-rate-limiter.replenishRate
属性定义了每秒钟允许多少个请求(不算放弃的请求)。这是令牌桶被填充的速度。
redis-rate-limiter.burstCapacity
属性是一个用户在一秒钟内允许的最大请求数(不算放弃的请求)。这是令牌桶可以容纳的令牌数量。将此值设置为零会阻止所有请求。
redis-rate-limiter.requestedTokens
属性是指一个请求要花费多少令牌。这是为每个请求从桶中提取的令牌数量,默认为 1
。
一个稳定的速率是通过在 replenishRate
和 burstCapacity
中设置相同的值来实现的。可以通过设置高于补给率的 burstCapacity
来允许临时的突发。在这种情况下,速率限制器需要在突发之间允许一些时间(根据 replenishRate
),因为连续两次突发会导致请求被放弃(HTTP 429 - Too Many Requests
)。下面的列表配置了一个 redis-rate-limiter
。
低于 1个请求/s 的速率限制是通过将 replenishRate
设置为想要的请求数, requestTokens
设置为秒数,burstCapacity
设置为 replenishRate
和 requestTokens
的乘积来完成的。例如,设置 replenishRate=1
,requestedTokens=60
,burstCapacity=60
,结果是1个请求/分钟的限制。
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
下面的例子在Java中配置了一个 KeyResolver
。
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
这定义了每个用户的请求率限制为10。爆发20次是允许的,但是,在下一秒,只有10个请求可用。KeyResolver
是一个简单的,获得 user
请求参数。
不建议在生产中使用这个方法 |
你也可以把速率限制器定义为一个实现 RateLimiter
接口的bean。在配置中,你可以用SpEL来引用bean的名字。#{@myRateLimiter}
是一个SpEL表达式,它引用了一个名为 myRateLimiter
的 bean。下面的清单定义了一个速率限制器,它使用了前面清单中定义的 KeyResolver
。
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@myRateLimiter}"
key-resolver: "#{@userKeyResolver}"
6.23. RewriteLocationResponseHeader
RewriteLocationResponseHeader
GatewayFilter
工厂修改 Location
响应头的值,通常是为了去掉后台的特定细节。它需要 stripVersionMode
、locationHeaderName
、hostValue
和 protocolsRegex
参数。下面的清单配置了一个 RewriteLocationResponseHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: rewritelocationresponseheader_route
uri: http://example.org
filters:
- RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
例如,对于一个 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,version也不会被剥离。 -
AS_IN_REQUEST
: 只有当原始请求路径不包含version时,才会剥离version。 -
ALWAYS_STRIP
: version 总是被剥离,即使原始请求路径包含version 。
hostValue
参数,如果提供的话,将用于替换响应的 Location
头的 host:port
部分。如果没有提供,则使用 Host
请求头的值。
protocolsRegex
参数必须是一个有效的 regex String
,协议名称将与之匹配。如果没有匹配,过滤器不做任何事情。默认是 http|https|ftp|ftps
。
6.24. RewritePath
RewritePath
GatewayFilter
工厂接收一个路径 regexp
参数和一个 replacement
参数。这是用Java正则表达式来重写请求路径的一种灵活方式。下面的列表配置了一个 RewritePath
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://example.org
predicates:
- Path=/red/**
filters:
- RewritePath=/red/?(?<segment>.*), /$\{segment}
对于请求路径为 /red/blue
的情况,在进行下游请求之前将路径设置为 /blue
。注意,由于YAML的规范,$
应该被替换成 $\
。
6.25. RewriteResponseHeader
RewriteResponseHeader
GatewayFilter
工厂接受 name
、regexp
和 replacement
参数。它使用Java正则表达式,以一种灵活的方式重写响应头的值。下面的例子配置了一个 RewriteResponseHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: rewriteresponseheader_route
uri: https://example.org
filters:
- RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***
对于一个 /42?user=ford&password=omg!what&flag=true
的header值,在发出下游请求后,它被设置为 /42?user=ford&password=***&flag=true
。因为YAML规范,你必须用 $\
来表示 $
。
6.26. SaveSession
SaveSession
GatewayFilter
工厂在转发下游调用之前强制进行 WebSession::save
操作。这在使用类似 Spring Session 的懒数据存储时特别有用,因为你需要确保在进行转发调用之前已经保存了Session状态。下面的例子配置了一个 SaveSession
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: save_session
uri: https://example.org
predicates:
- Path=/foo/**
filters:
- SaveSession
如果你将 Spring Security 与 Spring Session 集成,并希望确保安全细节(security detail)已被转发到远程进程,这一点至关重要。
6.27. SecureHeaders
根据 这篇博客的建议,SecureHeaders
GatewayFilter
工厂在响应中添加了一些头信息。
以下header信息(显示为其默认值)被添加。
-
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
-
frame-options
-
content-type-options
-
referrer-policy
-
content-security-policy
-
download-options
-
permitted-cross-domain-policies
要禁用默认值,请用逗号分隔的值设置 spring.cloud.gateway.filter.secure-headers.disable
属性,如下。
spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security
需要使用 secure header 的小写全名来禁用它。 |
6.28. SetPath
SetPath
GatewayFilter
工厂接受一个路径模板参数。它提供了一种简单的方法,通过允许模板化的路径段来操作请求路径。这使用了 Spring Framework 的URI模板。允许多个匹配段。下面的例子配置了一个 SetPath
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- SetPath=/{segment}
对于请求路径为 /red/blue
的情况,在进行下行请求之前,将路径设置为 /blue
。
6.29. SetRequestHeader
SetRequestHeader
GatewayFilter
工厂接受 name
和 value
参数。下面的列表配置了一个 SetRequestHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: setrequestheader_route
uri: https://example.org
filters:
- SetRequestHeader=X-Request-Red, Blue
这 GatewayFilter
会替换(而不是添加)所有给定名称的 header 信息。
因此,如果下游服务器以 X-Request-Red:1234
响应,它将被替换为 X-Request-Red:Blue
,这就是下游服务会收到的。
SetRequestHeader
知道用于匹配路径或主机的URI变量。URI变量可以在值中使用,并在运行时被扩展。下面的例子配置了一个 SetRequestHeader
GatewayFilter
,它使用了一个变量。
spring:
cloud:
gateway:
routes:
- id: setrequestheader_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- SetRequestHeader=foo, bar-{segment}
6.30. SetResponseHeader
SetResponseHeader
GatewayFilter
工厂接受 name
和 value
参数。下面的列表配置了一个 SetResponseHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: https://example.org
filters:
- SetResponseHeader=X-Response-Red, Blue
这个 GatewayFilter
会替换(而不是添加)所有带有给定名称的头信息。
因此,如果下游服务器以 X-Response-Red:1234
响应,它将被替换为 X-Response-Red:Blue
,这就是网关客户端将收到的内容。
SetResponseHeader
知道用于匹配路径或主机的URI变量。URI变量可以在值中使用,并将在运行时被扩展。下面的例子配置了一个 SetResponseHeader
GatewayFilter
,它使用了一个变量。
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- SetResponseHeader=foo, bar-{segment}
6.31. SetStatus
SetStatus
GatewayFilter
工厂只接受一个参数,即 status
。它必须是一个有效的Spring HttpStatus
。它可以是 404
的int值或枚举的字符串表示: NOT_FOUND
。下面的列表配置了一个 SetStatus
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: setstatusstring_route
uri: https://example.org
filters:
- SetStatus=UNAUTHORIZED
- id: setstatusint_route
uri: https://example.org
filters:
- SetStatus=401
在这两种情况下,响应的HTTP状态被设置为401。
你可以配置 SetStatus
GatewayFilter
,使其在响应中的头中返回代理请求的原始HTTP状态代码。如果配置了以下属性,该头会被添加到响应中。
spring:
cloud:
gateway:
set-status:
original-status-header-name: original-http-status
6.32. StripPrefix
StripPrefix
GatewayFilter
工厂需要一个参数,即 parts
。parts
参数表示在向下游发送请求之前要从路径中剥离的部分的数量。下面的列表配置了一个 StripPrefix
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: https://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2
当通过网关向 /name/blue/red
发出请求时,向 nameservice
发出的请求看起来像 nameservice/red
。
6.33. Retry
Retry
GatewayFilter
工厂支持以下参数。
-
retries
: 应该尝试的重试次数。 -
statuses
: 应该重试的HTTP状态代码,用org.springframework.http.HttpStatus
表示。 -
methods
: 应该重试的HTTP方法,用org.springframework.http.HttpMethod
来表示。 -
series
: 要重试的状态代码系列,用org.springframework.http.HttpStatus.Series
表示。 -
exceptions
: 抛出的异常的列表,应该重试。 -
backoff
: 为重试配置的指数式backoff。重试是在backoff
间隔firstBackoff * (factor ^ n)
之后进行的,其中n
是迭代次数。如果配置了maxBackoff
,应用的最大 backoff 时间被限制在maxBackoff
。如果basedOnPreviousValue
为true
,则通过使用prevBackoff * factor
来计算backoff。
如果启用,为 Retry
filter 配置的默认值如下。
-
retries
: 三次 -
series
: 5XX系列 -
methods
: GET 请求 -
exceptions
:IOException
和TimeoutException
-
backoff
: disabled
下面配置了一个 Retry
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: retry_test
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
methods: GET,POST
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
当使用带有 forward: 前缀的URL的 retry filter 时,应仔细编写目标端点,以便在出现错误时,它不会做任何可能导致响应被发送到客户端并提交的事情。例如,如果目标端点是一个 @Controller ,目标controller方法不应该返回带有错误状态码的 ResponseEntity 。相反,它应该抛出一个 Exception 或发出一个错误信号(例如,通过 Mono.error(ex) 返回值),retry filter 可以被配置为通过重试来处理。
|
当对任何带有body的HTTP方法使用 retry filter 时,body将被缓存,网关将变得内存受限。body被缓存在 ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR 定义的请求属性(request attribute)中。该对象的类型是 org.springframework.core.io.buffer.DataBuffer 。
|
一个简化的 "快捷" 方式可以用一个 status
和 method
来添加。
以下两个例子是等同的。
spring:
cloud:
gateway:
routes:
- id: retry_route
uri: https://example.org
filters:
- name: Retry
args:
retries: 3
statuses: INTERNAL_SERVER_ERROR
methods: GET
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
- id: retryshortcut_route
uri: https://example.org
filters:
- Retry=3,INTERNAL_SERVER_ERROR,GET,10ms,50ms,2,false
6.34. RequestSize
当请求的大小超过允许的限制时,RequestSize
GatewayFilter
工厂可以限制请求到达下游服务。该过滤器需要一个 maxSize
参数。maxSize
是一个 DataSize
类型,所以值可以定义为一个数字,后面有一个可选的 DataUnit
后缀,如 'KB' 或’MB'。默认是 'B',表示字节。它是以字节为单位定义的请求的可允许的大小限制。下面的列表配置了一个 RequestSize
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
maxSize: 5000000
RequestSize
GatewayFilter
工厂将响应状态设置为 413 Payload Too Large
,当请求由于大小而被拒绝时,会有一个额外的头 errorMessage
。下面的例子显示了这样一个 errorMessage
。
errorMessage : Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB
如果在路由定义中没有提供filter参数,默认请求大小被设置为5MB。 |
6.35. SetRequestHostHeader
在某些情况下,host 头可能需要被重写。在这种情况下,SetRequestHostHeader
GatewayFilter
工厂可以将现有的 host
header 替换成指定的值。该过滤器需要一个 host
参数。下面的列表配置了一个 SetRequestHostHeader
GatewayFilter
。
spring:
cloud:
gateway:
routes:
- id: set_request_host_header_route
uri: http://localhost:8080/headers
predicates:
- Path=/headers
filters:
- name: SetRequestHostHeader
args:
host: example.org
SetRequestHostHeader
GatewayFilter
工厂将 host
头的值替换为 example.org
。
6.36. TokenRelay
Token Relay是指OAuth2消费者作为客户端,将传入的令牌转发给传出的资源请求。消费者可以是一个纯粹的客户端(如SSO应用程序)或一个资源服务器。
Spring Cloud Gateway 可以将 OAuth2 访问令牌转发到它所代理的服务的下游。为了在网关中添加这一功能,你需要像这样添加 TokenRelayGatewayFilterFactory
。
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("resource", r -> r.path("/resource")
.filters(f -> f.tokenRelay())
.uri("http://localhost:9000"))
.build();
}
或者,这样。
spring:
cloud:
gateway:
routes:
- id: resource
uri: http://localhost:9000
predicates:
- Path=/resource
filters:
- TokenRelay=
它将(除了登录用户和抓取令牌之外)把认证令牌传递给下游的服务(在这里是 /resource
)。
要为 Spring Cloud Gateway 启用这个功能,需要添加以下依赖
-
org.springframework.boot:spring-boot-starter-oauth2-client
它是如何工作的? {githubmaster}/src/main/java/org/springframework/cloud/gateway/security/TokenRelayGatewayFilterFactory.java[filter]从当前认证的用户中提取一个访问令牌,并将其放在下游请求的请求头中。
完整的工作样本见 该项目。
只有当适当的 spring.security.oauth2.client.* 属性被设置时, TokenRelayGatewayFilterFactory Bean才会被创建,这将触发 ReactiveClientRegistrationRepository Bean的创建。
|
TokenRelayGatewayFilterFactory 使用的 ReactiveOAuth2AuthorizedClientService 的默认实现使用了一个内存数据存储。如果你需要一个更强大的解决方案,你将需要提供你自己的实现 ReactiveOAuth2AuthorizedClientService 。
|
6.37. 默认 Filter
要添加一个filter并将其应用于所有路由,可以使用 spring.cloud.gateway.default-filters
。这个属性需要一个filter的列表。下面的列表定义了一组默认filter。
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin
7. 全局 Filter
GlobalFilter
接口的签名与 GatewayFilter
相同。这些是特殊的过滤器,有条件地应用于所有路由。
这个接口和它的用法在未来的里程碑版本中可能会有变化。 |
7.1. GlobalFilter
组合和 GatewayFilter
的顺序
当一个请求与路由匹配时,filter web handler将 GlobalFilter
的所有实例和 GatewayFilter
的所有路由特定实例添加到一个过滤链中。这个组合的过滤链由 org.springframework.core.Ordered
接口进行排序,你可以通过实现 getOrder()
方法来设置这个接口。
由于 Spring Cloud Gateway 区分了过滤器逻辑执行的 “pre” 和 “post” 阶段(见如何工作),优先级最高的过滤器在 “pre” 阶段是第一个,在 “post” 阶段是最后一个。
下面的列表配置了一个过滤器链。
@Bean
public GlobalFilter customFilter() {
return new CustomGlobalFilter();
}
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("custom global filter");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
7.2. Gateway Metrics(指标) Filter
要启用网关指标,请添加 spring-boot-starter-actuator
作为项目依赖。然后,默认情况下,只要 spring.cloud.gateway.metrics.enabled
属性没有被设置为 false
,网关指标过滤器就会运行。这个过滤器添加了一个名为 spring.cloud.gateway.requests
的定时器指标,标签如下。
-
routeId
: 路由(route) ID. -
routeUri
: API被路由到的URI。 -
outcome
: 结果,由 HttpStatus.Series 分类。 -
status
: 返回给客户端的请求的HTTP状态。 -
httpStatusCode
: 返回给客户端的请求的HTTP状态。 -
httpMethod
: 请求使用的HTTP方法。
此外,通过 spring.cloud.gateway.metrics.tags.path.enabled
属性(默认为 false
),你可以用路径标签(path tag)激活一个额外的指标。
-
path
: 请求的路径。
这些指标可以从 /actuator/metrics/spring.cloud.gateway.requests
中抓取,并可以很容易地与 Prometheus
集成,创建一个 Grafana dashboard。
要启用 Prometheus 端点,请将 micrometer-registry-prometheus 添加到项目依赖。
|
7.3. Local Response Cache Filter
如果 LocalResponseCache
的相关属性被启用(spring.cloud.gateway.filter.local-response-cache.enabled
),它就会运行,并为所有符合以下条件的响应激活一个本地缓存。
-
请求是一个无body的 GET。
-
响应具有以下状态代码之一。HTTP 200 (OK), HTTP 206 (Partial Content), 或 HTTP 301 (Moved Permanently)。
-
HTTP
Cache-Control
头允许缓存(这意味着它没有以下任何值。在请求中出现no-store
,在响应中出现no-store
或private
)。
它接受两个配置参数:
-
spring.cloud.gateway.filter.local-response-cache.size
。设置该路由驱逐条目的最大缓存大小(单位:KB、MB 和 GB)。 -
spring.cloud.gateway.filter.local-response-cache.timeToLive
。设置缓存条目过期的时间(用 s 表示秒、m 表示分钟、h 表示小时)。如果没有配置这些参数,但启用了全局过滤器,默认情况下,它为缓存的响应配置了5分钟的生存时间。
这个 filter 还实现了自动计算 HTTP Cache-Control
头中的 max-age
值。如果原始响应中存在 max-age
,则该值会以 timeToLive
配置参数中设置的秒数重写。在随后的调用中,这个值会以响应过期前的剩余秒数重新计算。
如果你的项目创建了自定义的 CacheManager Bean,它需要用 @Primary 来标记,或者用 @Qualifier 来注入。
|
7.4. Forward Routing Filter
ForwardRoutingFilter
在 exchange 属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
中寻找一个URI。如果URL有一个 forward scheme(如 forward:///localendpoint
),它就使用 Spring DispatcherHandler
来处理请求。请求URL的路径部分被前向URL中的路径所覆盖。未修改的原始URL被追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR
属性的列表中。
7.5. Netty Routing Filter
如果位于 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
exchange 属性中的URL有一个 http
或 https
scheme,Netty 路由过滤器就会运行。它使用Netty HttpClient
来进行下游代理请求。响应被放在 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR
exchange 属性中,供以后的过滤器使用。(还有一个实验性的 WebClientHttpRoutingFilter
,执行同样的功能,但不需要 Netty。)
7.6. Netty Write Response Filter
如果在 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR
exchange 属性中有一个 Netty HttpClientResponse
,NettyWriteResponseFilter
就会运行。它在所有其他过滤器完成后运行,并将代理响应写回网关客户端响应。(还有一个实验性的 WebClientWriteResponseFilter
,执行同样的功能,但不需要 Netty。)
7.7. ReactiveLoadBalancerClientFilter
ReactiveLoadBalancerClientFilter
在名为 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的 exchange 属性中寻找一个URI。如果URL有一个 lb
scheme(如 lb://myservice
),它将使用Spring Cloud ReactorLoadBalancer
将名称(本例中为 myservice
)解析为实际的主机和端口,并替换同一属性中的URI。未修改的原始URL被追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR
属性的列表中。过滤器也会在 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR
属性中查看是否等于 lb
,如果是,同样的规则也适用。下面的列表配置了一个 ReactiveLoadBalancerClientFilter
。
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**
默认情况下,当 ReactorLoadBalancer 无法找到一个服务实例时,会返回 503 。你可以通过设置 spring.cloud.gateway.loadbalancer.use404=true ,将网关配置为返回 404 。
|
从 ReactiveLoadBalancerClientFilter 返回的 ServiceInstance 的 isSecure 值覆盖了向网关发出的请求中指定的scheme。例如,如果请求通过 HTTPS 进入Gateway,但 ServiceInstance 表示它不安全,那么下游请求将通过 HTTP 进行。相反的情况也可以适用。然而,如果在网关配置中为路由指定了 GATEWAY_SCHEME_PREFIX_ATTR ,那么前缀将被剥离,来自路由 URL 的结果scheme将覆盖 ServiceInstance 的配置。
|
Gateway 支持所有的 LoadBalancer 功能。你可以在 Spring Cloud Commons 文档中阅读更多关于它们的信息。
|
7.8. RouteToRequestUrl
Filter
如果在 ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR
exchange 属性中有一个 Route
对象,RouteToRequestUrlFilter
会运行。它创建了一个新的URI,基于请求URI,但用 Route
对象的URI属性进行更新。新的URI被放在 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
exchange 属性中。
如果URI有一个scheme 前缀(prefix),比如 lb:ws://serviceid
,lb
scheme就会从URI中剥离出来,放在 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR
中,以便以后在过滤器链中使用。
7.9. Websocket Routing Filter
如果位于 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
exchange 属性中的URL有 ws
或 wss
scheme,则运行 websocket 路由过滤器。它使用Spring WebSocket 基础设施将 websocket 请求转发到下游。
你可以通过给URI加上 lb
的前缀来实现websockets的负载均衡,比如 lb:ws://serviceid
。
如果你使用 SockJS 作为普通HTTP的后备方案,你应该配置一个普通的HTTP路由以及websocket路由。 |
下面配置了一个websocket路由过滤器。
spring:
cloud:
gateway:
routes:
# SockJS route
- id: websocket_sockjs_route
uri: http://localhost:3001
predicates:
- Path=/websocket/info/**
# Normal Websocket route
- id: websocket_route
uri: ws://localhost:3001
predicates:
- Path=/websocket/**
7.10. 标记 exchange 为已路由
在网关路由了一个 ServerWebExchange
后,它通过在 exchange 属性中添加 gatewayAlreadyRouted
来标记该交换为 “routed” 。一旦一个请求被标记为已路由,其他路由过滤器就不会再对该请求进行路由,基本上是跳过过滤器。有一些方便的方法,你可以用来标记一个 exchange 为已路由,或者检查一个 exchange 是否已经被路由。
-
ServerWebExchangeUtils.isAlreadyRouted
接收一个ServerWebExchange
对象并检查它是否已经被 "路由"。 -
ServerWebExchangeUtils.setAlreadyRouted
接收一个ServerWebExchange
对象并将其标记为 "已路由"。
8. HttpHeadersFilter
HttpHeadersFilter
在向下游发送请求之前被应用于请求,例如在 NettyRoutingFilter
。
8.1. Forwarded Headers Filter
Forwarded
Headers Filter 创建一个 Forwarded
header来发送给下游服务。它将当前请求的 Host
header、scheme和port添加到任何现有的 Forwarded
头中。
8.2. RemoveHopByHop Headers Filter
RemoveHopByHop
Headers Filter 从转发的请求中移除header信息。被移除的header信息的默认列表来自 IETF。
-
Connection
-
Keep-Alive
-
Proxy-Authenticate
-
Proxy-Authorization
-
TE
-
Trailer
-
Transfer-Encoding
-
Upgrade
要改变这一点,请将 spring.cloud.gateway.filter.remove-hop-by-hop.headers
属性设置为要删除的 header 名称列表。
8.3. XForwarded Headers Filter
XForwarded
Headers Filter 创建各种 X-Forwarded-*
头,以发送到下游的服务。它使用当前请求的 Host
header、scheme、port 和路径来创建各种 header。
创建单个 header 可以由以下布尔属性控制(默认为 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
附加多个 header 可以由以下布尔属性控制(默认为 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
9. TLS 和 SSL
网关可以通过遵循通常的 Spring server configuration 来监听 HTTPS 请求。下面的例子显示了如何做到这一点。
server:
ssl:
enabled: true
key-alias: scg
key-store-password: scg1234
key-store: classpath:scg-keystore.p12
key-store-type: PKCS12
你可以将网关路由到HTTP和HTTPS后端。如果你要路由到HTTPS后端,你可以通过以下配置将网关配置为信任所有下游的证书。
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true
使用不安全的 trust manager 不适合于生产。对于生产部署,你可以用一组已知的证书来配置网关,它可以通过以下配置来信任。
spring:
cloud:
gateway:
httpclient:
ssl:
trustedX509Certificates:
- cert1.pem
- cert2.pem
如果 Spring Cloud Gateway 没有配置受信任的证书,就会使用默认的 trust store(你可以通过设置 javax.net.ssl.trustStore
系统属性来覆盖它)。
9.1. TLS 握手
网关维护着一个client pool,它用来路由到后端。当通过HTTPS进行通信时,客户端发起了一个TLS握手。一些 timeout 配置与这个握手相关。你可以对这些 timeouts 进行配置,如下(默认值)。
spring:
cloud:
gateway:
httpclient:
ssl:
handshake-timeout-millis: 10000
close-notify-flush-timeout-millis: 3000
close-notify-read-timeout-millis: 0
10. 配置(Configuration)
Spring Cloud Gateway 的配置是由 RouteDefinitionLocator
实例的集合驱动的。下面的列表显示了 RouteDefinitionLocator
接口的定义。
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}
默认情况下,PropertiesRouteDefinitionLocator
通过使用Spring Boot的 @ConfigurationProperties
机制加载属性。
前面的配置例子都使用了一种快捷方式,即使用位置参数而不是命名参数。下面的两个例子是等价的。
spring:
cloud:
gateway:
routes:
- id: setstatus_route
uri: https://example.org
filters:
- name: SetStatus
args:
status: 401
- id: setstatusshortcut_route
uri: https://example.org
filters:
- SetStatus=401
对于网关的某些用途来说,属性已经足够了,但一些生产用例会从外部来源(如数据库)加载配置中受益。未来的里程碑版本将有基于 Spring Data Repository 的 RouteDefinitionLocator
实现,如 Redis、MongoDB和Cassandra。
10.1. RouteDefinition 指标
要启用 RouteDefinition
指标,请添加 spring-boot-starter-actuator
依赖。然后,默认情况下,只要 spring.cloud.gateway.metrics.enabled
属性被设置为 true
,这些指标就会可用。将添加一个名为 spring.cloud.gateway.routes.count
的指标,其值是 RouteDefinitions
的数量。这个指标将从 /actuator/metrics/spring.cloud.gateway.routes.count
中获得。
11. 路由元数据配置
你可以通过使用元数据为每个路由配置额外的参数,如下所示。
spring:
cloud:
gateway:
routes:
- id: route_with_metadata
uri: https://example.org
metadata:
optionName: "OptionValue"
compositeObject:
name: "value"
iAmNumber: 1
你可以从一个 exchange 所获取所有的元数据属性,如下所示
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
// get all metadata properties
route.getMetadata();
// get a single metadata property
route.getMetadata(someKey);
12. Http超时(timeout)配置
可以为所有路由配置Http超时(响应和连接),也可以为每个特定的路由重写。
12.1. 全局 timeout
要配置全局http超时。
connect-timeout
必须以毫秒为单位指定。
响应超时必须以 java.time.Duration
的形式指定。
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s
12.2. 每个路由的 timeout
要配置每个路由的超时。
connect-timeout
必须以毫秒为单位指定。
response-timeout
必须以毫秒为单位指定。
- id: per_route_timeouts
uri: https://example.org
predicates:
- name: Path
args:
pattern: /delay/{timeout}
metadata:
response-timeout: 200
connect-timeout: 200
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeBuilder){
return routeBuilder.routes()
.route("test1", r -> {
return r.host("*.somehost.org").and().path("/somepath")
.filters(f -> f.addRequestHeader("header1", "header-value-1"))
.uri("http://someuri")
.metadata(RESPONSE_TIMEOUT_ATTR, 200)
.metadata(CONNECT_TIMEOUT_ATTR, 200);
})
.build();
}
路由的 response-timeout
为负值,将禁用全局 response-timeout
值。
- id: per_route_timeouts uri: https://example.org predicates: - name: Path args: pattern: /delay/{timeout} metadata: response-timeout: -1
12.3. Route 的Fluent式 Java API
为了允许在Java中进行简单的配置,RouteLocatorBuilder
Bean包括一个fluent API。下面的列表显示了它是如何工作的。
// static imports from GatewayFilters and RoutePredicates
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
return builder.routes()
.route(r -> r.host("**.abc.org").and().path("/image/png")
.filters(f ->
f.addResponseHeader("X-TestHeader", "foobar"))
.uri("http://httpbin.org:80")
)
.route(r -> r.path("/image/webp")
.filters(f ->
f.addResponseHeader("X-AnotherHeader", "baz"))
.uri("http://httpbin.org:80")
.metadata("key", "value")
)
.route(r -> r.order(-1)
.host("**.throttle.org").and().path("/get")
.filters(f -> f.filter(throttle.apply(1,
1,
10,
TimeUnit.SECONDS)))
.uri("http://httpbin.org:80")
.metadata("key", "value")
)
.build();
}
这种风格也允许更多的自定义谓词断言。RouteDefinitionLocator
bean 类定义的谓词使用逻辑 and
来组合。通过使用fluent Java API,你可以在 Predicate
类上使用 and()
、or()
和 negate()
操作符。
12.4. DiscoveryClient
路由服务的注册与发现
你可以将网关配置为基于在 DiscoveryClient
兼容服务注册中心上注册的服务来创建路由。
要启用这一点,请设置 spring.cloud.gateway.discovery.locator.enabled=true
并确保 DiscoveryClient
实现(如 Netflix Eureka、Consul 或 Zookeeper)位于 classpath 上并已启用。
12.4.1. 为 DiscoveryClient
路由配置谓词和过滤器
默认情况下,网关为用 DiscoveryClient
创建的路由定义了一个谓词和过滤器。
默认谓词是用 /serviceId/**
pattern 定义的路径谓词,其中 serviceId
是来自 DiscoveryClient
的服务的 ID
。
默认的过滤器是一个重写路径过滤器,使用 regex /serviceId/?(?<remaining>.*)
和替换词 /${remaining}
。这在请求被发送到下游之前从路径中剥离了服务ID。
如果你想定制 DiscoveryClient
路由使用的谓词或过滤器,请设置 spring.cloud.gateway.discovery.locator.predicates[x]
和 spring.cloud.gateway.discovery.locator.filters[y]
。这样做时,如果你想保留该功能,你需要确保包括前面显示的默认谓词和过滤器。下面的例子显示了这是什么样子。
spring.cloud.gateway.discovery.locator.predicates[0].name: Path spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]: "'/'+serviceId+'/**'" spring.cloud.gateway.discovery.locator.predicates[1].name: Host spring.cloud.gateway.discovery.locator.predicates[1].args[pattern]: "'**.foo.com'" spring.cloud.gateway.discovery.locator.filters[0].name: CircuitBreaker spring.cloud.gateway.discovery.locator.filters[0].args[name]: serviceId spring.cloud.gateway.discovery.locator.filters[1].name: RewritePath spring.cloud.gateway.discovery.locator.filters[1].args[regexp]: "'/' + serviceId + '/?(?<remaining>.*)'" spring.cloud.gateway.discovery.locator.filters[1].args[replacement]: "'/${remaining}'"
13. Reactor Netty 访问日志
要启用 Reactor Netty 访问日志,请设置 -Dreactor.netty.http.server.accessLogEnabled=true
。
它必须是一个 Java System Property,而不是一个Spring Boot属性。 |
你可以配置日志系统,使其有一个单独的访问日志文件。下面的例子创建了一个Logback配置。
<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
<file>access_log.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="accessLog" />
</appender>
<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
<appender-ref ref="async"/>
</logger>
14. CORS 配置
你可以配置网关来控制全局或每个路由的 CORS 行为。两者都提供同样的可能性。
14.1. Global CORS 配置
“global” CORS配置是对 Spring Framework CorsConfiguration
的URL模式的映射。下面的例子配置了 CORS。
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET
在前面的例子中,对于所有GET请求的路径,允许来自 docs.spring.io
的请求的CORS请求。
要为未被某些网关路由谓词处理的请求提供相同的 CORS 配置,请将 spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping
属性设为 true
。当你试图支持 CORS 预检请求,而你的路由谓词因为 HTTP 方法是 options
而不能评估为 true
时,这很有用。
14.2. 路由的 CORS 配置
“route” configuration 允许将CORS直接应用于带有key CORS
的路由作为元数据。像全局配置一样,这些属性属于 Spring Framework CorsConfiguration
。
如果路由中没有 Path 谓词,则将应用 '/**'。
|
spring:
cloud:
gateway:
routes:
- id: cors_route
uri: https://example.org
predicates:
- Path=/service/**
metadata:
cors
allowedOrigins: '*'
allowedMethods:
- GET
- POST
allowedHeaders: '*'
maxAge: 30
15. Actuator API
通过 /gateway
执行器端点,你可以监控并与 Spring Cloud Gateway 应用进行互动。要进行远程访问,端点必须在应用程序属性中 enabled 并 通过HTTP或JMX暴露。下面的列表显示了如何做到这一点。
management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway
15.1. 冗长的 Actuator 格式
在Spring Cloud Gateway中增加了一种新的、更啰嗦的格式。它为每个路由添加了更多细节,让你查看与每个路由相关的谓词和过滤器,以及任何可用的配置。下面的例子配置了 /actuator/gateway/routes
。
[
{
"predicate": "(Hosts: [**.addrequestheader.org] && Paths: [/headers], match trailing slash: true)",
"route_id": "add_request_header_test",
"filters": [
"[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
"[[AddRequestHeader X-Request-Foo = 'Bar'], order = 1]",
"[[PrefixPath prefix = '/httpbin'], order = 2]"
],
"uri": "lb://testservice",
"order": 0
}
]
这个功能默认是启用的。要禁用它,请设置以下属性。
spring.cloud.gateway.actuator.verbose.enabled=false
在未来的版本中,这将默认为 true
。
15.2. 检索路由过滤器
本节详细介绍了如何检索路由过滤器,包括:
15.2.1. 全局过滤器(Global Filter)
要检索应用于所有路由的 全局过滤器,请向 /actuator/gateway/globalfilters
发出一个GET请求。得到的响应类似于以下内容。
{ "org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter@77856cc5": 10100, "org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@4f6fd101": 10000, "org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@32d22650": -1, "org.springframework.cloud.gateway.filter.ForwardRoutingFilter@106459d9": 2147483647, "org.springframework.cloud.gateway.filter.NettyRoutingFilter@1fbd5e0": 2147483647, "org.springframework.cloud.gateway.filter.ForwardPathFilter@33a71d23": 0, "org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@135064ea": 2147483637, "org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@23c05889": 2147483646 }
响应包含全局过滤器的细节,这些过滤器已经到位。对于每个全局过滤器,有一个过滤器对象的字符串表示(例如,org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter@77856cc5
)以及过滤器链中的相应 顺序。
15.2.2. 路由过滤器(Route Filter)
要检索应用于路由的 GatewayFilter
工厂,请向 /actuator/gateway/routefilters
发出一个 GET
请求。得到的响应类似于以下内容。
{ "[AddRequestHeaderGatewayFilterFactory@570ed9c configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null, "[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]": null, "[SaveSessionGatewayFilterFactory@4449b273 configClass = Object]": null }
响应包含应用于任何特定路由的 GatewayFilter
工厂的细节。对于每个工厂,有一个相应对象的字符串表示(例如,[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]
)。请注意,nul
是由于 endpoint controller 的不完整实现,因为它试图设置过滤器链中的对象的顺序,这不适用于 GatewayFilter
工厂对象。
15.3. 刷新路由缓存
要清除路由缓存,请向 /actuator/gateway/refresh
发出一个 POST
请求。该请求返回一个200,没有响应体。
15.4. 检索网关中自定义的路由
要检索网关中定义的路由,请向 /actuator/gateway/routes
发出一个 GET
请求。得到的响应类似于以下内容。
[{ "route_id": "first_route", "route_object": { "predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@1e9d7e7d", "filters": [ "OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory$$Lambda$436/674480275@6631ef72, order=0}" ] }, "order": 0 }, { "route_id": "second_route", "route_object": { "predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@cd8d298", "filters": [] }, "order": 0 }]
响应包含网关中定义的所有路由的详细信息。下表描述了响应中每个元素的结构(每个都是一个路由)。
Path | Type | Description |
---|---|---|
|
String |
路由 ID。 |
|
Object |
路由的 predicate。 |
|
Array |
应用于路由的 |
|
Number |
路由顺序。 |
15.5. 检索指定的路由
要检索一条路由的信息,请向 /actuator/gateway/routes/{id}
发出一个 GET
请求(例如,/actuator/gateway/routes/first_route
)。得到的响应类似于下面的内容。
{ "id": "first_route", "predicates": [{ "name": "Path", "args": {"_genkey_0":"/first"} }], "filters": [], "uri": "https://www.uri-destination.org", "order": 0 }
下表描述了响应的结构。
Path | Type | Description |
---|---|---|
|
String |
路由 ID。 |
|
Array |
路由谓词的集合。每一项都定义了谓词的名称和参数。 |
|
Array |
应用于路由的Filter集合。 |
|
String |
路由的目的地URI。 |
|
Number |
路由的Order。 |
15.6. 创建和删除一个指定的路由
要创建一个路由,请向 /gateway/routes/{id_route_to_create}
发出一个 POST
请求,该请求包含一个指定路由字段的JSON BODY(见 检索指定的路由)。
要删除一条路由,请向 /gateway/routes/{id_route_to_delete}
发出 DELETE
请求。
15.7. 回顾:所有端点列表
下面的表格总结了 Spring Cloud Gateway 的执行器(actuator)端点(注意,每个端点都有 /actuator/gateway
作为基本路径)。
ID | HTTP Method | Description |
---|---|---|
|
GET |
显示应用于路由的全局过滤器的列表。 |
|
GET |
显示应用于特定路由的 |
|
POST |
清除路由缓存。 |
|
GET |
显示网关中定义的路由列表。 |
|
GET |
显示指定路由的信息。 |
|
POST |
添加一个新的路由到网关。 |
|
DELETE |
从网关删除一个路由。 |
15.8. 在多个网关实例之间共享路由
Spring Cloud Gateway 提供两种 RouteDefinitionRepository
实现。第一个是 InMemoryRouteDefinitionRepository
,它只存在于一个 Gateway 实例的内存中。这种类型的 Repository 不适合在多个Gateway实例中填充路由。
为了在 Spring Cloud Gateway 实例的集群中共享路由,可以使用 RedisRouteDefinitionRepository
。要启用这种 repository,必须将以下属性设置为 true
: spring.cloud.gateway.redis-route-definition-repository.enabled
。与 RedisRateLimiter
过滤器工厂一样,它需要使用 spring-boot-starter-data-redis-reactive
Spring Boot starter。
16. 问题排查
本节涵盖了你在使用 Spring Cloud Gateway 时可能出现的常见问题。
16.1. 日志级别
在 DEBUG
和 TRACE
级别,以下logger可能包含有价值的故障排除信息。
-
org.springframework.cloud.gateway
-
org.springframework.http.server.reactive
-
org.springframework.web.reactive
-
org.springframework.boot.autoconfigure.web
-
reactor.netty
-
redisratelimiter
16.2. Wiretap
Reactor Netty 的 HttpClient
和 HttpServer
可以启用 wiretap。当与 reactor.netty
日志级别设置为 DEBUG
或 TRACE
相结合时,它能够记录信息,例如通过路由发送和接收的header信息和body。要启用 wiretap
,请分别为 HttpServer
和 HttpClient
设置 spring.cloud.gateway.httpserver.wiretap=true
或 spring.cloud.gateway.httpclient.wiretap=true
。
17. 开发手册
这些是编写网关的一些自定义组件的基本指南。
17.1. 编写自定义路由(Route)谓语(Predicate)工厂
自定义 Route Predicate,你需要提供一个实现了 RoutePredicateFactory
的 bean。有一个抽象的类叫做 AbstractRoutePredicateFactory
,你可以继承它。
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
public MyRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
// grab configuration from Config object
return exchange -> {
//grab the request
ServerHttpRequest request = exchange.getRequest();
//take information from the request to see if it
//matches configuration.
return matches(config, request);
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
17.2. 编写自定义 GatewayFilter 工厂
自定义 GatewayFilter
,你必须提供一个实现了 GatewayFilterFactory
的bean。你可以继承一个名为 AbstractGatewayFilterFactory
的抽象类。下面的例子展示了如何做到这一点。
@Component
public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {
public PreGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// grab configuration from Config object
return (exchange, chain) -> {
//If you want to build a "pre" filter you need to manipulate the
//request before calling chain.filter
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
//use builder to manipulate the request
return chain.filter(exchange.mutate().request(builder.build()).build());
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
@Component
public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {
public PostGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// grab configuration from Config object
return (exchange, chain) -> {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
//Manipulate the response in some way
}));
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
17.2.1. 在配置中命名自定义 Filter 和引用
自定义Filter的类名应该以 GatewayFilterFactory
结尾。
例如,要在配置文件中引用一个名为 Something
的过滤器,该过滤器必须在一个名为 SomethingGatewayFilterFactory
的类中。
可以创建一个没有 GatewayFilterFactory 后缀的网关过滤器,如 AnotherThing 类。这个过滤器可以在配置文件中被引用为 AnotherThing 。这不是一个被支持的命名惯例,这种语法可能在未来的版本中被删除。请更新过滤器的名称,使其符合要求。
|
17.3. 编写自定义全局过滤器
要编写一个自定义的全局过滤器,你必须提供一个实现了 GlobalFilter
接口的Bean。这将把过滤器应用于所有的请求。
下面的例子分别说明了如何设置全局 pre 过滤器和 post 过滤器。
@Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> exchange.getPrincipal()
.map(Principal::getName)
.defaultIfEmpty("Default User")
.map(userName -> {
//adds header to proxied request
exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", userName).build();
return exchange;
})
.flatMap(chain::filter);
}
@Bean
public GlobalFilter customGlobalPostFilter() {
return (exchange, chain) -> chain.filter(exchange)
.then(Mono.just(exchange))
.map(serverWebExchange -> {
//adds header to response
serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked": "It did not work");
return serverWebExchange;
})
.then();
}
18. 通过使用Spring MVC或Webflux构建一个简单的网关
下面描述的是另一种风格的网关。之前的文档都不适用于下面的内容。 |
Spring Cloud Gateway 提供了一个名为 ProxyExchange
的实用对象。你可以在普通的 Spring web handler 中作为方法参数使用它。它通过反映 HTTP 动词的方法支持基本的下游 HTTP exchange。在MVC中,它还支持通过 forward()
方法转发到本地处理程序。要使用 ProxyExchange
,在你的classpath中包含正确的模块(spring-cloud-gateway-mvc
或 spring-cloud-gateway-webflux
)。
下面的MVC例子将一个到 /test
的请求代理到一个远程服务器。
@RestController
@SpringBootApplication
public class GatewaySampleApplication {
@Value("${remote.home}")
private URI home;
@GetMapping("/test")
public ResponseEntity<?> proxy(ProxyExchange<byte[]> proxy) throws Exception {
return proxy.uri(home.toString() + "/image/png").get();
}
}
下面的例子用Webflux实现相同的效果。
@RestController
@SpringBootApplication
public class GatewaySampleApplication {
@Value("${remote.home}")
private URI home;
@GetMapping("/test")
public Mono<ResponseEntity<?>> proxy(ProxyExchange<byte[]> proxy) throws Exception {
return proxy.uri(home.toString() + "/image/png").get();
}
}
ProxyExchange
上的便利方法使处理方法能够发现并增强传入请求的URI路径。例如,你可能想提取路径的尾部元素,将它们传递到下游。
@GetMapping("/proxy/path/**")
public ResponseEntity<?> proxyPath(ProxyExchange<byte[]> proxy) throws Exception {
String path = proxy.path("/proxy/path/");
return proxy.uri(home.toString() + "/foos/" + path).get();
}
Spring MVC和Webflux的所有功能都可用于网关 handler method。因此,你可以注入请求头和查询参数,例如,你可以通过 mapping 注解中的声明来限制传入的请求。关于这些功能的更多细节,请参见Spring MVC中的 @RequestMapping
文档。
你可以通过使用 ProxyExchange
的 header()
方法在下游响应中添加头信息。
你也可以通过给 get()
方法(和其他方法)添加一个 mapper 来操作响应头(以及 response 中你喜欢的其他东西)。mapper 是一个 Function
,它接收传入的 ResponseEntity
并将其转换为传出的。
对 "敏感" header(默认为 cookie
和 授 authorization
)和 “proxy”(x-forwarded-*
)header 提供了一流的支持,这些标头不会被传递到下游。
19. AOT 和 原生镜像(Native Image)的支持
从 4.0.0
开始,Spring Cloud Gateway支持 Spring AOT 转换和原生镜像。
如果你使用负载均衡路由,你需要明确定义你的 LoadBalancerClient 服务 ID。你可以通过使用 @LoadBalancerClient 注解的 value 或 name 属性或作为 spring.cloud.loadbalancer.eager-load.clients 属性的值来实现。
|