本站(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.xSpring WebFluxProject 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 Diagram

客户端向 Spring Cloud Gateway 发出请求。如果Gateway处理程序映射确定一个请求与路由相匹配,它将被发送到Gateway Web处理程序。这个处理程序通过一个特定于该请求的过滤器链来运行该请求。过滤器被虚线分割的原因是,过滤器可以在代理请求发送之前和之后运行逻辑。所有的 "pre" (前)过滤器逻辑都被执行。然后发出代理请求。在代理请求发出后,"post" (后)过滤器逻辑被运行。

在路由中定义的没有端口的URI,其HTTP和HTTPS URI的默认端口值分别为80和443。

4. 配置Route ,Predicate ,Filter 工厂

有两种方式来配置谓词和过滤器:快捷方式和完全展开的参数。下面的大多数例子都使用快捷方式。

名称和参数名称作为 code 列在每一节的第一或第二句中。参数通常按照快捷方式配置所需的顺序列出。

4.1. 快捷方式的配置

快捷方式配置由过滤器名称(filter name),后面跟一个等号 (=),然后是用逗号 (,) 分隔的参数值。

application.yml
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是一个键值对的映射,用于配置谓词或过滤器。

application.yml
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路由谓词。

Example 1. application.yml
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路由谓词。

Example 2. application.yml
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 路由谓词工厂需要两个参数,datetime1datetime2,它们是java ZonedDateTime 对象。这个谓词匹配发生在 datetime1 之后和 datetime2 之前的请求。datetime2 的参数必须在 datetime1 之后。下面的例子配置了一个 between 路由谓词。

Example 3. application.yml
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之前的任何请求。这对维护窗口可能是有用的。

Cookie 路由谓词工厂接受两个参数,即 cookie name 和一个 regexp(这是一个Java正则表达式)。这个谓词匹配具有给定名称且其值符合正则表达式的cookie。下面的例子配置了一个cookie路由谓词工厂。

Example 4. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

这个路由匹配有一个名为 chocolate 的cookie,其值符合 ch.p 正则表达式的请求。

Header 路由谓词工厂需要两个参数,header 和一个 regexp(这是一个Java正则表达式)。这个谓词与具有给定名称且其值与正则表达式相匹配的 header 匹配。下面的例子配置了一个 header 路由谓词。

Example 5. application.yml
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 路由谓词。

Example 6. application.yml
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.orgbeta.somehost.orgwww.anotherhost.org,则该路由匹配。

这个谓词提取URI模板变量(比如前面例子中定义的 sub)作为名称和值的映射,并将其放在 ServerWebExchange.getAttributes() 中,key值定义在 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE。然后这些值就可以被 GatewayFilter 工厂使用了。

5.7. Method

Method 路由谓词工厂接受一个 methods 参数,它是一个或多个参数:要匹配的HTTP方法。下面的例子配置了一个 method 路由谓词。

Example 7. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

如果请求方式是 GETPOST,则该路由匹配。

5.8. Path

Path 路由谓词工厂需要两个参数:一个Spring PathMatcher patterns 的list和一个可选的flag matchTrailingSlash(默认为 true)。下面的例子配置了一个path路由谓词。

Example 8. application.yml
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 路由谓词。

Example 9. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

如果请求包含一个 green 的查询参数,前面的路由就会匹配。

application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=red, gree.

如果请求中包含一个 red 的查询参数,其值与 gree. 表达式相匹配,那么路由就会匹配。例如: greengreet

5.10. RemoteAddr

RemoteAddr 路由谓词工厂接受一个 sources 集合(最小长度为1),它是CIDR注解(IPv4或IPv6)字符串,如 192.168.0.1/16(其中 192.168.0.1 是一个IP地址,16 是一个子网掩码)。下面的例子配置了一个RemoteAddr路由谓词。

Example 10. application.yml
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 结果

[Integer.MIN_VALUE,0]

(无效, 初始化时会抛出 IllegalArgumentException 异常)

1

0.0.0.3

2

0.0.0.2

3

0.0.0.1

[4, Integer.MAX_VALUE]

0.0.0.1

下面的例子显示了如何用Java实现同样的配置。

Example 11. GatewayConfig.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 路由谓语工厂需要两个参数:groupweight(一个int值)。weight 是按 group 计算的。下面的例子配置了一个 weight 路由谓词。

Example 12. application.yml
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 路由谓词。

Example 13. application.yml
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 工厂需要一个 namevalue 参数。下面的例子配置了一个 AddRequestHeader GatewayFilter

Example 14. application.yml
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,它使用一个变量。

Example 15. application.yml
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 工厂接受一个由冒号分隔的 namevalue 键值对的集合。下面的例子配置了一个 AddRequestHeadersIfNotPresent GatewayFilter

Example 16. application.yml
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:blueX-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

Example 17. application.yml
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需要一个 namevalue 参数。下面的例子配置了一个 AddRequestParameter GatewayFilter

Example 18. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        filters:
        - AddRequestParameter=red, blue

这将为所有匹配的请求在下游请求的查询字符串中添加 red=blue

AddRequestParameter 知道用于匹配路径或主机的URI变量。URI变量可以在值中使用,并在运行时被扩展。下面的例子配置了一个 AddRequestParameter GatewayFilter,它使用了一个变量。

Example 19. application.yml
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 工厂需要一个 namevalue 参数。下面的例子配置了一个 AddResponseHeader GatewayFilter

Example 20. application.yml
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,它使用了一个变量。

Example 21. application.yml
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

Example 22. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: https://example.org
        filters:
        - CircuitBreaker=myCircuitBreaker

要配置熔断器,请参阅你所使用的底层熔断器实现的配置。

Spring Cloud CircuitBreaker 过滤器还可以接受一个可选的 fallbackUri 参数。目前,只支持 forward: 模式的URI。如果fallback被调用,请求将被转发到URI所匹配的控制器。下面的例子配置了这样一个fallback。

Example 23. application.yml
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来实现相同的配置,如下。

Example 24. Application.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

Example 25. application.yml
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,如下所示。

Example 26. application.yml
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 枚举的字符串表示。

Example 27. application.yml
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"
Example 28. Application.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").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();
}
Example 29. application.yml
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

Example 30. application.yml
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-CredentialsAccess-Control-Allow-Origin 响应头的情况下,这将删除重复的值。

DedupeResponseHeader 过滤器还接受一个可选的 strategy 参数。接受的值是 RETAIN_FIRST(默认)、RETAIN_LASTRETAIN_UNIQUE

6.8. FallbackHeaders

通过 FallbackHeaders 工厂,你可以在转发到外部应用程序中的 fallbackUri 的请求的header中添加Spring Cloud CircuitBreaker的执行异常细节,如以下场景。

Example 31. application.yml
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-storeprivate)。

  • 如果响应已经被缓存,并且在 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();
}

或者,这样:

application.yaml
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 工厂接受 fromHeadertoHeader 参数。它创建一个新的命名header(toHeader),并从传入的http请求的现有命名头(fromHeader)中提取值。如果输入的header不存在,过滤器没有任何影响。如果新的命名header信息已经存在,它的值就会被增加新的值。下面的例子配置了一个 MapRequestHeader

Example 32. application.yml
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

Example 33. application.yml
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

Example 34. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: https://example.org
        filters:
        - PreserveHostHeader

6.16. RedirectTo

RedirectTo GatewayFilter 工厂需要两个参数,statusurlstatus 参数应该是一个300系列的重定向 HTTP 状态码,如301。url 参数应该是一个有效的URL。这就是 Location header 的值。对于相对重定向,你应该使用 uri: no://op 作为路由定义的uri。下面的列表配置了一个 RedirectTo GatewayFilter

Example 35. application.yml
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

Example 36. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: removejsonattributes_route
        uri: https://example.org
        filters:
        - RemoveJsonAttributesResponseBody=id,color

这从根层的JSON body中删除了属性 "id" 和 "color"。

下面的例子配置了一个 RemoveJsonAttributesResponseBody GatewayFilter,它使用了可选的最后参数。

Example 37. application.yml
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

Example 38. application.yml
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

Example 39. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: removerequestparameter_route
        uri: https://example.org
        filters:
        - RemoveRequestParameter=red

这将在向下游发送之前删除 red 参数。

6.20. RemoveResponseHeader

RemoveResponseHeader GatewayFilter 工厂需要一个 name 参数。它是要被移除的 header 的名称。下面的列表配置了一个 RemoveResponseHeader GatewayFilter

Example 40. application.yml
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 工厂接受 maxSizeerrorHeaderName 参数。maxSize 参数是请求头(包括key和value)所允许的最大数据大小。errorHeaderName 参数设置包含错误信息的响应头的名称,默认为 "errorMessage"。下面的列表配置了一个 RequestHeaderSize GatewayFilter

Example 41. application.yml
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 的接口。

Example 42. KeyResolver.java
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-keytruefalse)和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code 属性来调整这种行为。

RequestRateLimiter 不能用 "快捷方式" 来配置。下面的例子是无效的。

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

一个稳定的速率是通过在 replenishRateburstCapacity 中设置相同的值来实现的。可以通过设置高于补给率的 burstCapacity 来允许临时的突发。在这种情况下,速率限制器需要在突发之间允许一些时间(根据 replenishRate),因为连续两次突发会导致请求被放弃(HTTP 429 - Too Many Requests)。下面的列表配置了一个 redis-rate-limiter

低于 1个请求/s 的速率限制是通过将 replenishRate 设置为想要的请求数, requestTokens 设置为秒数,burstCapacity 设置为 replenishRaterequestTokens 的乘积来完成的。例如,设置 replenishRate=1requestedTokens=60burstCapacity=60,结果是1个请求/分钟的限制。

Example 44. application.yml
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

Example 45. Config.java
@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

Example 46. application.yml
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 响应头的值,通常是为了去掉后台的特定细节。它需要 stripVersionModelocationHeaderNamehostValueprotocolsRegex 参数。下面的清单配置了一个 RewriteLocationResponseHeader GatewayFilter

Example 47. application.yml
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_STRIPAS_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

Example 48. application.yml
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 工厂接受 nameregexpreplacement 参数。它使用Java正则表达式,以一种灵活的方式重写响应头的值。下面的例子配置了一个 RewriteResponseHeader GatewayFilter

Example 49. application.yml
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

Example 50. application.yml
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

Example 51. application.yml
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 工厂接受 namevalue 参数。下面的列表配置了一个 SetRequestHeader GatewayFilter

Example 52. application.yml
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,它使用了一个变量。

Example 53. application.yml
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 工厂接受 namevalue 参数。下面的列表配置了一个 SetResponseHeader GatewayFilter

Example 54. application.yml
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,它使用了一个变量。

Example 55. application.yml
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

Example 56. application.yml
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状态代码。如果配置了以下属性,该头会被添加到响应中。

Example 57. application.yml
spring:
  cloud:
    gateway:
      set-status:
        original-status-header-name: original-http-status

6.32. StripPrefix

StripPrefix GatewayFilter 工厂需要一个参数,即 partsparts 参数表示在向下游发送请求之前要从路径中剥离的部分的数量。下面的列表配置了一个 StripPrefix GatewayFilter

Example 58. application.yml
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。如果 basedOnPreviousValuetrue,则通过使用 prevBackoff * factor 来计算backoff。

如果启用,为 Retry filter 配置的默认值如下。

  • retries: 三次

  • series: 5XX系列

  • methods: GET 请求

  • exceptions: IOExceptionTimeoutException

  • backoff: disabled

下面配置了一个 Retry GatewayFilter

Example 59. application.yml
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

一个简化的 "快捷" 方式可以用一个 statusmethod 来添加。

以下两个例子是等同的。

Example 60. application.yml
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

Example 61. application.yml
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

Example 62. application.yml
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

App.java
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("resource", r -> r.path("/resource")
                    .filters(f -> f.tokenRelay())
                    .uri("http://localhost:9000"))
            .build();
}

或者,这样。

application.yaml
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。

Example 63. application.yml
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” 阶段是最后一个。

下面的列表配置了一个过滤器链。

Example 64. ExampleConfiguration.java
@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-storeprivate)。

它接受两个配置参数:

  • 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有一个 httphttps scheme,Netty 路由过滤器就会运行。它使用Netty HttpClient 来进行下游代理请求。响应被放在 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR exchange 属性中,供以后的过滤器使用。(还有一个实验性的 WebClientHttpRoutingFilter,执行同样的功能,但不需要 Netty。)

7.6. Netty Write Response Filter

如果在 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR exchange 属性中有一个 Netty HttpClientResponseNettyWriteResponseFilter 就会运行。它在所有其他过滤器完成后运行,并将代理响应写回网关客户端响应。(还有一个实验性的 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

Example 65. application.yml
spring:
  cloud:
    gateway:
      routes:
      - id: myRoute
        uri: lb://service
        predicates:
        - Path=/service/**
默认情况下,当 ReactorLoadBalancer 无法找到一个服务实例时,会返回 503。你可以通过设置 spring.cloud.gateway.loadbalancer.use404=true,将网关配置为返回 404
ReactiveLoadBalancerClientFilter 返回的 ServiceInstanceisSecure 值覆盖了向网关发出的请求中指定的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://serviceidlb scheme就会从URI中剥离出来,放在 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 中,以便以后在过滤器链中使用。

7.9. Websocket Routing Filter

如果位于 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR exchange 属性中的URL有 wswss scheme,则运行 websocket 路由过滤器。它使用Spring WebSocket 基础设施将 websocket 请求转发到下游。

你可以通过给URI加上 lb 的前缀来实现websockets的负载均衡,比如 lb:ws://serviceid

如果你使用 SockJS 作为普通HTTP的后备方案,你应该配置一个普通的HTTP路由以及websocket路由。

下面配置了一个websocket路由过滤器。

Example 66. application.yml
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

默认删除的header是:
  • 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 请求。下面的例子显示了如何做到这一点。

Example 67. application.yml
server:
  ssl:
    enabled: true
    key-alias: scg
    key-store-password: scg1234
    key-store: classpath:scg-keystore.p12
    key-store-type: PKCS12

你可以将网关路由到HTTP和HTTPS后端。如果你要路由到HTTPS后端,你可以通过以下配置将网关配置为信任所有下游的证书。

Example 68. application.yml
spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          useInsecureTrustManager: true

使用不安全的 trust manager 不适合于生产。对于生产部署,你可以用一组已知的证书来配置网关,它可以通过以下配置来信任。

Example 69. application.yml
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 进行配置,如下(默认值)。

Example 70. application.yml
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 接口的定义。

Example 71. RouteDefinitionLocator.java
public interface RouteDefinitionLocator {
    Flux<RouteDefinition> getRouteDefinitions();
}

默认情况下,PropertiesRouteDefinitionLocator 通过使用Spring Boot的 @ConfigurationProperties 机制加载属性。

前面的配置例子都使用了一种快捷方式,即使用位置参数而不是命名参数。下面的两个例子是等价的。

Example 72. application.yml
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. 路由元数据配置

你可以通过使用元数据为每个路由配置额外的参数,如下所示。

Example 73. application.yml
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 的形式指定。

global http timeouts example
spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s

12.2. 每个路由的 timeout

要配置每个路由的超时。 connect-timeout 必须以毫秒为单位指定。 response-timeout 必须以毫秒为单位指定。

per-route http timeouts configuration via configuration
      - id: per_route_timeouts
        uri: https://example.org
        predicates:
          - name: Path
            args:
              pattern: /delay/{timeout}
        metadata:
          response-timeout: 200
          connect-timeout: 200
per-route timeouts configuration using Java DSL
import static org.springframework.cloud.gateway.support.RouteMetadataUtils.CONNECT_TIMEOUT_ATTR;
import static org.springframework.cloud.gateway.support.RouteMetadataUtils.RESPONSE_TIMEOUT_ATTR;

      @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。下面的列表显示了它是如何工作的。

Example 74. GatewaySampleApplication.java
// 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]。这样做时,如果你想保留该功能,你需要确保包括前面显示的默认谓词和过滤器。下面的例子显示了这是什么样子。

Example 75. application.properties
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配置。

Example 76. logback.xml
    <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。

Example 77. application.yml
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 谓词,则将应用 '/**'。
Example 78. application.yml
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暴露。下面的列表显示了如何做到这一点。

Example 79. application.properties
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
  }
]

这个功能默认是启用的。要禁用它,请设置以下属性。

Example 80. application.properties
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

route_id

String

路由 ID。

route_object.predicate

Object

路由的 predicate。

route_object.filters

Array

应用于路由的 GatewayFilter 工厂

order

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

id

String

路由 ID。

predicates

Array

路由谓词的集合。每一项都定义了谓词的名称和参数。

filters

Array

应用于路由的Filter集合。

uri

String

路由的目的地URI。

order

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

globalfilters

GET

显示应用于路由的全局过滤器的列表。

routefilters

GET

显示应用于特定路由的 GatewayFilter 工厂的列表。

refresh

POST

清除路由缓存。

routes

GET

显示网关中定义的路由列表。

routes/{id}

GET

显示指定路由的信息。

routes/{id}

POST

添加一个新的路由到网关。

routes/{id}

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. 日志级别

DEBUGTRACE 级别,以下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 的 HttpClientHttpServer 可以启用 wiretap。当与 reactor.netty 日志级别设置为 DEBUGTRACE 相结合时,它能够记录信息,例如通过路由发送和接收的header信息和body。要启用 wiretap,请分别为 HttpServerHttpClient 设置 spring.cloud.gateway.httpserver.wiretap=truespring.cloud.gateway.httpclient.wiretap=true

17. 开发手册

这些是编写网关的一些自定义组件的基本指南。

17.1. 编写自定义路由(Route)谓语(Predicate)工厂

自定义 Route Predicate,你需要提供一个实现了 RoutePredicateFactory 的 bean。有一个抽象的类叫做 AbstractRoutePredicateFactory,你可以继承它。

MyRoutePredicateFactory.java
@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 的抽象类。下面的例子展示了如何做到这一点。

Example 81. PreGatewayFilterFactory.java
@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
    }

}
PostGatewayFilterFactory.java
@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-mvcspring-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 文档。

你可以通过使用 ProxyExchangeheader() 方法在下游响应中添加头信息。

你也可以通过给 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 注解的 valuename 属性或作为 spring.cloud.loadbalancer.eager-load.clients 属性的值来实现。

20. 配置属性

要查看所有与Spring Cloud Gateway相关的配置属性列表,请参见 附录