Spring Cloud Gateway

Spring Cloud Gateway

1、基本概念

  • Route:网关的基本模块。由id、目标url、断言(predicates)集合以及过滤器(filters)集合组成
  • Predicate:是一个Java8的 Predicate函数 ,输入类型是一个 Spring Framework ServerWebExchange 。这时的你可以匹配 HTTP 请求的任何内容,例如请求头或参数。
  • Filter:特定工厂构建的 GatewayFilter 实例。在这里可以在发送下游请求之前或之后修改请求和响应

2、工作原理图

在这里插入图片描述
客户端向 Spring Cloud Gateway 发出请求。如果 网关映射处理器(gateway handler mapping) 确认请求与路由匹配,则将其发送到 网关 Web 处理器(gateway web handler)。web处理器会通过一条定义好的过滤链来处理请求。过滤器被虚线分隔的原因是过滤器可以在**发送代理请求之前(pre)之后(post)**运行。先执行所有“pre”过滤逻辑。然后发出代理请求。发出代理请求后,运行“post”筛选器逻辑。

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

3、配置网关的断言集和过滤集

有两种方式方式配置predicates 和 filters。
    --快捷方式
    --完全参数展开方式
 名称和参数名称将作为代码列在每个部分的前一两句话中。参数通常按照快捷方式配置所需的顺序列出。

3.1 快捷方式配置

示例 :application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - Cookie=mycookie,mycookievalue

3.2 完全展开的参数

示例

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - name: Cookie
          args:
            name: mycookie
            regexp: mycookievalue

4、predicates factories

Spring Cloud Gateway作为Spring WebFlux HandlerMapping基础设施的一部分来匹配路由,Spring Cloud Gateway包括许多内置的路由predicate工厂。所有这些predicate都匹配HTTP请求的不同属性。可以使用逻辑和语句组合多个路由predicate工厂。

4.1 After

After:带一个参数–datetime(Java 的 ZonedDateTime)。匹配的是定义时间参数之后发送的请求

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

4.2 Before

Before:参数–datetime,定义时间参数之前发送的请求

4.3 Between

Between:两个参数 datetime1、datetime2。datetime2必须大于datetime1
匹配定义两个参数中间这个时间段

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]

4.4 Cookie

Cookie:两个参数 name,regexp(Java regular expression)。
匹配具有给定名称且其值与正则表达式匹配的cookie。

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

该路由匹配具有名为chocolate的cookie且其值与ch.p正则表达式匹配的请求。

4.5 Header

Header :两个参数 name,regexp。
匹配具有给定名称且其值与正则表达式匹配的header。

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

如果请求有一个名为X-Request-Id的头,该头的值匹配\d+正则表达式(也就是说,它有一个或多个数字的值),则该路由匹配。

4.6 Host

Host:一个参数 主机名 patterns 列表

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

4.7 Method

Method: 请求方法

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

4.8 Path

Path :路径 两个参数 – 路径集合
matchTrailingSlash (可选参数,默认true):是否匹配带有尾部斜杠(/)的请求

例如:若 matchTrailingSlash 设置为false 则不会匹配 /get/info/ 这样的路径
若 matchTrailingSlash 设置为true 则 /get/info 与 /get/info/ 都可以匹配

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

这个 predicate 会将url中的参数 ({segement}) 提取成一个的map<name,value> , 并其放在ServerWebExchangeUtils中。然后,GatewayFilter工厂可以使用这些值

Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);

String segment = uriVariables.get("segment");

4.9 Query

query: 查询路由 两个参数 param(必须的), regexp(可选的)

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请求参数 且 value 与 gree. 这个正则匹配 则路由匹配

4.10 RemoteAddr

RemoteAddr :远程路径 参数:一个源列表 (min size 1)。这些源是cidr符号(IPv4或IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是IP地址,16是子网掩码)。

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

4.11 Weight

Weight :权重路由 两个参数 group 和 weight

在开发或者测试的时候,或者线上发布,线上服务多版本控制的时候,需要对服务提供权重路由,最常见的使用就是,一个服务有两个版本,旧版本V1,新版本v2。在线上灰度的时候,需要通过网关动态实时推送,路由权重信息。比如95%的流量走服务v1版本,5%的流量走服务v2版本

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% 走高权重 20% 走低权重

4.11.1 修改远程路径解析方式

默认情况下,RemoteAddr会匹配请求中的远程地址。如果网关层在代理层后的话,则无法匹配实际的客户端地址。
可以通过自定义一个 RemoteAddressResolver 来定制远程地址的解析
Spring Cloud Gateway带有一个非默认的远程地址解析器,基于 X-Forwarded-For header 的 XForwardedRemoteAddressResolver

XForwardedRemoteAddressResolver:有两个静态构造方法,采取不同的安全措施。
XForwardedRemoteAddressResolver::trustAll 返回一个XForwardedRemoteAddressResolver:总是选择 X-Forwarded-For 的header中的第一个 ip地址。这种方法很容易受到欺骗,因为恶意客户端可能会为X-Forwarded-For设置一个初始值,该值将被解析器接受
XForwardedRemoteAddressResolver::maxTrustedIndex:选择一个与Spring Cloud Gateway前面运行的可信基础设施数量相关的索引。例如,如果Spring Cloud Gateway只能通过HAProxy访问,那么应该使用值1。如果在访问Spring Cloud Gateway之前需要可信基础设施的两个跃点,那么应该使用值2。

5、Gateway Filter Factories

路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器的作用域是特定的路由。Spring Cloud Gateway包括许多内置的GatewayFilter工厂。

5.1 AddRequestHeader

添加请求头。一个 name-value 的参数

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue

所有向下游传递的的request请求都添加上一个X-Request-red:blue 的请求头

AddRequestHeader 能够识别用于匹配 path 和 host 的url变量。URI变量可以在值中使用,并在运行时展开。

示例

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - AddRequestHeader=X-Request-Red, Blue-{segment}

5.2 AddRequestParameter

添加请求参数 添加一个 name-value 参数

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

会给所有匹配的向下游传递的 request 添加一个 red=bule 的参数

AddRequestParameter 能够识别用于匹配 path 和 host 的url变量。URI变量可以在值中使用,并在运行时展开。

5.3 AddResponseHeader

添加响应头 添加一个 name-value 的 响应头

5.4 DedupeResponseHeader

剔除重复的响应头 。有一个 name 参数 和一个可选的 strategy 参数。name 可以包含标题名称列表,以空格分隔。

spring:
  cloud:
    gateway:
      routes:
      - id: dedupe_response_header_route
        uri: https://example.org
        filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

我们在Gateway以及微服务上都设置了CORS(解决跨域)header,如果不做任何配置,请求 -> 网关 -> 微服务,获得的响应就是这样
Access-Control-Allow-Credentials: true, true
Access-Control-Allow-Origin: https://hxmec.com, https://hxmec.com
若添加了该 filters 则会去除响应头中的重复值

DedupeResponseHeader过滤器还接受可选的strategy参数。去重策略 可接受的值为

RETAIN_FIRST(默认值):保留第一个值
RETAIN_LAST:保留最后一个值
RETAIN_UNIQUE:保留所有唯一值,以它们第一次出现的顺序保留

5.5 Spring Cloud CircuitBreaker GatewayFilter factory

Spring Cloud CircuitBreaker GatewayFilter使用Spring Cloud CircuitBreaker api 将 Gateway 路由包裹在断路器中。Spring Cloud CircuitBreaker支持多个库,可以与Spring Cloud Gateway一起使用。

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

详情参见 Spring Cloud Circuit Breaker

5.6 FallbackHeaders

允许 转发给外部应用程序中的fallbackUri的请求的头中 添加 Spring Cloud CircuitBreake执行异常的细节信息

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

在这个例子中,当请求lb://ingredients降级后,FallbackHeadersfilter 会将 CircuitBreaker 的异常信息,通过Test-Header带给http://localhost:9994服务。
你也可以使用默认的header,也可以像上面一下配置修改header的名字:

  • executionExceptionTypeHeaderName (“Execution-Exception-Type”)
  • executionExceptionMessageHeaderName (“Execution-Exception-Message”)
  • rootCauseExceptionTypeHeaderName (“Root-Cause-Exception-Type”)
  • rootCauseExceptionMessageHeaderName (“Root-Cause-Exception-Message”)

5.7 MapRequestHeader

两个参数:fromHeader toHeader 。创建一个新的命名头(toHeader),并从传入的http请求中提取现有的命名头(fromHeader)。如果输入头不存在,则过滤器没有影响。如果新命名的标头已经存在,则用新值扩展它的值

spring:
  cloud:
    gateway:
      routes:
      - id: map_request_header_route
        uri: https://example.org
        filters:
        - MapRequestHeader=Blue, X-Request-Red

从上游的请求头 bule 中获取 value 创建一个X-Request-Red< value > 的请求头 添加到下游请求中

5.8 PrefixPath

一个 prefix 参数 请求添加前缀

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - PrefixPath=/mypath

会在所有匹配的 request 之前添加 /mypath 。例如 请求/hello 会发送到 /mypath/hello

5.9 PreserveHostHeader

无参 。留原始请求的host头信息,并原封不动的转发出去,而不是被gateway的http客户端重置。

spring:
  cloud:
    gateway:
      routes:
        - id: eureka-client-provider #路由的ID
          uri: lb://eureka-client-provider
          predicates:
            - Path=/provider/** # 路由规则
          filters:
            - StripPrefix=1
            # 保留原始请求的host头信息,并原封不动的转发出去,而不是被gateway的http客户端重置。
            - PreserveHostHeader

设置了 PreserveHostHeader过滤器,过滤器会把ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE = true保存到 ServerWebExchange的属性中(attributes:是个 Map 结构)。在 NettyRoutingFilter 过滤器中,会取出该属性,判断值为 true,就取出原始请求头中的 Host 添加进新的请求(request)中。

5.10 RequestRateLimiter

RequestRateLimiter 使用 RateLimiter 实现是否允许继续执行当前请求。如果不允许继续执行,则返回HTTP 429 - Too Many Requests (默认情况下)。
该过滤器接受一个可选的 keyResolver 参数 和 特定的速率限制器的参数(Redis RateLimiter)。
keyResolver 是一个是实现了 KeyResolver 接口的bean。在配置中通过sqEL #{@myKeyResolver} (myKeyResolver – bean name ) 引用

KeyResolver 接口:

public interface KeyResolver {
    Mono<String> resolve(ServerWebExchange exchange);
}

KeyResolver 接口允许通过可插拔策略 来获取 限制请求的 key
KeyResolver 的默认实现是 PrincipalNameKeyResolver,它会从 ServerWebExchange检索 Principal并调用 Principal.getName()。
默认情况下,如果 KeyResolver 找不到 Key,请求就会被拒绝。也可以通过设置以下属性来调整该行为

spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true or false)
spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code

Tips: RequestRateLimiter 不支持快捷配置。以下配置是无效的

spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}

5.10.1 The Redis RateLimiter

Redis RateLimiter 的实现基于 Stripe 。需要引入 spring-boot-starter-data-redis-reactive 的依赖
算法使用的是 令牌桶算法
属性:

  • redis-rate-limiter.replenishRate :允许处理请求数/秒,不丢弃请求。该值是令牌桶的流入速率。
  • redis-rate-limiter.burstCapacity:用户在一秒钟内允许做的最大请求数。该值是令牌桶持有的令牌数。设置值为 0 将阻止所有请求。
  • redis-rate-limiter.requestedTokens:每个请求消耗的令牌数。该值是请求从 令牌桶 中获取的令牌数,默认为 1。

replenishRate 和 burstCapacity 设为相同值就能获得一个稳定的速率。
burstCapacity 大于 replenishRate 的话,可以允许临时的流量爆发。在这种情况下,在突发期间,速率限制器需要开启一段时间(取决于replenishRate)。因为两次连续突发会导致请求丢失(HTTP 429 -太多请求)。

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

5.11 RedirectTo

两个参数 :status 和 url 。status 应该是一个300系列的重定向 HTTP 状态码 。url参数应该是一个有效的url。这是Location报头的值。
对于相对重定向,应该使用 uri: no://op 作为路由定义的 url

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - RedirectTo=302, https://acme.org

这将发送一个带有 Location:https://acme.org头的 状态为302 的 url 执行重定向。

5.12 RemoveRequestHeader

移除请求头 参数:name

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: https://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

将会在向下游传递请求之前 移除 名为 X-Request-Foo 的请求头

5.13 RemoveResponseHeader

移出响应头 参数:name

spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: https://example.org
        filters:
        - RemoveResponseHeader=X-Response-Foo

在返回网关客户端之前 移除名为 X-Response-Foo 的响应头

5.14 RemoveRequestParameter

移除请求参数 参数:name --需要移除的请求参数的参数名

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

向下游传递之前会移除 名为 red 的请求参数

5.15 RewritePath

重写路径 参数:regexp(路径匹配规则),replacement (替换规则)。它使用Java正则表达式 灵活的 重写请求路径。

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: https://example.org
        predicates:
        - Path=/red/**
        filters:
        - RewritePath=/red/?(?<segment>.*), /$\{segment}

对于/red/blue的请求路径,这会在发出下游请求之前将路径设置为/blue。注意,根据YAML规范,$ 应该被替换为 $\。

5.16 RewriteLocationResponseHeader

修改 response 的 location 的值 ,通常是为了消除后端特定的细节。接收stripVersionMode, locationHeaderName, hostValue 和 protocolsRegex 参数。

stripVersionMode:

  • NEVER_STRIP:版本信息不会被剥离,即使原始请求路径不包含版本
  • AS_IN_REQUEST :仅当原始请求路径不包含任何版本时,才会剥离版本【默认】
  • ALWAYS_STRIP:即使原始请求路径包含版本,也会剥离版本
    参数 hostValue,如果提供,会替换 Response Header Location 值中的 host:port 部分;如果不提供,则会使用 Request 的 Host 作为默认值
    参数 protocolRegex,协议会与该值匹配,如果不匹配,过滤器不回做任何操作,默认值 http|https|ftp|ftps

5.17 RewriteResponseHeader

重写响应头 参数:name,regexp,replacement 。使用 Java 正则表达式提供一种灵活的方式来重写响应头中的值。

spring:
  cloud:
    gateway:
      routes:
      - id: rewriteresponseheader_route
        uri: https://hxmec.com
        filters:
        - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***

示例中,一个头的值为 /42?user=ford&password=omg!what&flag=true,在发出下游请求后,将此头设置为 /42?user=ford&password=***&flag=true。注意使用 $\ 代表 $,这是 YAML 格式指定的。

5.18 SaveSession

保存 Session,在向下游服务转发请求之前强制执行 WebSession::save操作

spring:
  cloud:
    gateway:
      routes:
      - id: save_session
        uri: http://www.hxmec.com
        predicates:
        - Path=/foo/**
        filters:
        - SaveSession

如果你将 Spring Session 与 Spring Security与集成在了一起,并且希望确保安全细节能够转发到远程进程,这一项非常重要。

5.19 SecureHeaders

添加多个响应头。详细建议 Everything you need to know about HTTP security headers
会添加以下的头参数(带默认值):

  • X-Xss-Protection:1 (mode=block)
  • Strict-Transport-Security (max-age=631138519)
  • X-Frame-Options (DENY)
  • X-Content-Type-Options (nosniff)
  • Referrer-Policy (no-referrer)
    -Content-Security-Policy (default-src ‘self’ https:; font-src ‘self’ https: data:; img-src ‘self’ https: data:; object-src ‘none’; script-src https:; style-src ‘self’ https: ‘unsafe-inline)’
  • X-Download-Options (noopen)
  • X-Permitted-Cross-Domain-Policies (none)

要更改默认值,请在spring.cloud.gateway.filter中设置适当的属性。secure-headers名称空间。以下属性可用:

  • xss-protection-header
  • strict-transport-security
  • x-frame-options
  • x-content-type-options
  • referrer-policy
  • content-security-policy
  • x-download-options
  • x-permitted-cross-domain-policies

要禁用默认值,请设置spring.cloud.gateway.filter.secure-headers。禁用带有逗号分隔值的属性。

spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security

Tips:secure-header 需要使用小写的全名来禁用

5.20 SetPath

接受一个路径模板参数。提供一个 路径模板片段 来操作 request 路径,使用Spring框架的URI模板。允许提供多个 路径模板片段

spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - SetPath=/{segment}

如果 路径为 /red/bule ,则将会将路径 设置成 /bule 向下游传递

5.21 SetRequestHeader

设置 请求头 。接收一个 name-value 参数

spring:
  cloud:
    gateway:
      routes:
      - id: setrequestheader_route
        uri: https://example.org
        filters:
        - SetRequestHeader=X-Request-Red, Blue

设置 X-Request-Red 请求头的值为 Bule 而不是新增一个请求头。
SetRequestHeader 能够获取 用于匹配的 路径或主机 的url 中的变量,并在运行时展开

spring:
  cloud:
    gateway:
      routes:
      - id: setrequestheader_route
        uri: https://example.org
        predicates:
        - Host: {segment}.myhost.org
        filters:
        - SetRequestHeader=foo, bar-{segment}

5.22 SetResponseHeader

设置 响应头。参数; name-value

spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: https://example.org
        filters:
        - SetResponseHeader=X-Response-Red, Blue

设置 响应头X-Response-Red 的值为 Bule
SetRequestHeader 能够获取 用于匹配的 路径或主机 的url 中的变量,并在运行时展开

5.23 SetStatus

设置状态 参数:status,必须是个可用的 HttpStatus

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: https://example.org
        filters:
        - SetStatus=BAD_REQUEST
      - id: setstatusint_route
        uri: https://example.org
        filters:
        - SetStatus=401

此示例,设置响应的 HTTP status 为 401。
可以配置 SetStatus 返回 响应中的代理请求的 原始状态码

spring:
  cloud:
    gateway:
      set-status:
        original-status-header-name: original-http-status

5.24 StripPrefix

截断路径前缀 参数:parts 截取前缀的个数

spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: https://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

如果请求 为 /name/red/bule ,那么像下游传递yi的就是 /bule

5.25 Retry

重试请求。可以接收以下参数

  • retries :重试次数
  • statuses:哪种 HTTP 状态码应该重试。用 org.springframework.http.HttpStatus 表示
  • methods:哪种 HTTP 方法应该重试
  • series:一系列的应该重试的 状态码
  • exceptions :应该重试的异常列表
  • backoff:重试指数。retry 会在 一个 backoff 时间间隔后执行。backoff时间间隔的计算公式:firstBackoff * (factor ^ n) ,n 是重复次数。如果配置了 maxBackoff ,那么 backoff 间隔时间 将会被限制在maxBackoof 之内。如果 配置了 basedOnPreviousValue 为true ,backoff 的计算方式则为 prevBackoff * factor

如果Retry启动。那么以下是默认的配置

  • retries:3
  • series:5XX
  • methods:GET
  • exceptions: IOException,TimeoutException
  • backoff:disabled
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

Tips: 当将 Retry 过滤器与带有forward:前缀的 URL 一起使用时,应仔细编写目标端点,以便在发生错误的情况下,它不会做任何可能导致响应发送到客户端并提交的操作。 例如,如果目标端点是带注释的控制器,则目标控制器方法不应返回带有错误状态代码的 ResponseEntity。 相反,它应该引发 Exception 或发出错误信号(例如,通过Mono.error(ex)返回值),可以配置重试过滤器来进行重试处理。

warning:当将重试过滤器与任何带有 body 的 HTTP方法一起使用时,body 将被缓存,并且网关将受到内存的限制。 body 将缓存在 ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR定义的请求属性中,对象的类型是org.springframework.core.io.buffer.DataBuffer。

5.26 RequestSize

当请求大小超过限制时,RequestSize会限制请求到达下游服务。
参数:maxSize。DataSize类型,值 可以 定义为 一个数字带一个单位。例如KB、MB。默认byte 。它是以字节为单位定义的请求允许的大小限制。

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

默认大小限制为5MB

5.27 SetRequestHostHeader

在某些情况下,需要重写 host header 。在这种情况下,SetRequestHostHeader 能够替换 已经存在的 host header。
参数: host

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

将 host header 的值替换为 example.org

5.28 ModifyRequestBody

在请求传递给下游之前 修改 请求体。只支持Java 配置

@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;
    }
}

5.29 ModifyResponseBody

5.30 Token Relay

5.31 Default Filters

6 全局过滤器(Global Filters)

GlobalFilter 接口与 gateway filter 有着相同的特性。这些是有条件地应用于所有路由的特殊 filter。

6.1 组合 全局过滤器 和 GatewayFilter 排序

当请求与路由匹配时,web过滤处理器会将所有的 全局过滤器(GlobalFilter)实例 和 路由中定义的 filter实例 添加到 过滤链 中。这个组合过滤器链是按照org.springframework.core.Ordered接口排序的,可以通过实现getOrder()方法来设置排序。
Spring Cloud Gateway区分了过滤器逻辑执行的”pre”和”post”阶段,所以优先级高的过滤器将会在pre阶段最先执行,优先级最低的过滤器则在post阶段最后执行。

6.2 Forward Routing Filter

ForwardRoutingFilter 会去查看exchange的ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR属性值(该值就是配置文件里设置的 ruotes : url),如果这个URL有一个forward schema(方案?策略),比如:forward://localendpoint,那么它会使用Spirng的DispatcherHandler 处理该请求。URL请求的 路径 部分,会被forward URL中的路径覆盖。未修改的原始URL,会被追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 属性中。

6.3 ReactiveLoadBalancerClientFilter

ReactiveLoadBalancerClientFilter 会去查看exchange的ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR属性值(该值就是配置文件里设置的routes : url),如果该 URL 有 lb scheme(方案?策略),例如:lb://myservice,这个filter 就会使用 spring cloud ReactorLoadBalancer 将 myservice 解析成实际的host和port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的内容。原始地址会追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 中。该过滤器还会查看 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 属性,如果发现该属性的值是 lb ,也会执行相同逻辑。

默认情况下,如果无法在 LoadBalancer 找到指定服务的实例,那么会返回503(对应如上的例子,找不到service实例,就返回503);可使用 spring.cloud.gateway.loadbalancer.use404=true 让其返回404。

LoadBalancer 返回的 ServiceInstance 的 isSecure 的值,会覆盖请求的scheme。举个例子,如果请求打到Gateway上使用的是 HTTPS ,但 ServiceInstance 的 isSecure 是false,那么下游收到的则是HTTP请求,反之亦然。然而,如果该路由指定了 GATEWAY_SCHEME_PREFIX_ATTR 属性,那么前缀将会被剥离,并且路由URL中的scheme会覆盖 ServiceInstance 的配置

6.4 The Netty Routing Filter

6.5 The Netty Write Response Filter

6.6 The RouteToRequestUrl Filter

6.7 The Websocket Routing Filter

6.8 The Gateway Metrics Filter

6.9 Marking An Exchange As Routed

7、HttpHeadersFilters

HttpHeadersFilters 在请求被发送到下游之前被执行

7.1 Forwarded Headers Filter

8、TLS 和 SSL

网关可以通过遵循通常的 Spring 服务器配置来侦听 HTTPS 上的请求。

9、配置

Spring Cloud Gateway 的配置是由 一系列的 RouteDefinitionLocator 实例驱动的。

public interface RouteDefinitionLocator {
    Flux<RouteDefinition> getRouteDefinitions();
}

默认情况下,PropertiesRouteDefinitionLocator 通过 springboot 的 @ConfigurationProperties 加载属性

就网关的一般用途而言,属性是足够用了。但是生产中有时候需要从外部源加载配置(例如:数据库)。所以未来版本将添加 基于 Spring Data Repositories 的 RouteDefinitionLocator 实现,例如 Redis、MongoDB 和 Cassandra。

10、路由元数据配置

可以使用 元数据(metadata) 为每个路由配置附加参数

spring:
  cloud:
    gateway:
      routes:
      - id: route_with_metadata
        uri: https://example.org
        metadata:
          optionName: "OptionValue"
          compositeObject:
            name: "value"
          iAmNumber: 1

可以从交换中获取所有元数据属性

Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
// 获取元数据的全部属性
route.getMetadata();
// 获取指定的元数据属性
route.getMetadata(someKey);

11、Http超时配置

可以为所有路由配置Http超时(响应和连接),也可以为每个特定路由配置。

11.1 全局超时配置

配置全局超时:

  • connect-timeout :必须以毫秒为单位
  • response-timeout:必须指定为 java.time.Duration
spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s

11.2 单路由超时配置

配置单路由超时:

  • 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

Java Bean方式配置 单路由超时

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();
      }

11.3 Fluent(链式) Java Routes API

为了实现在 Java 中的简易配置,RouteLocatorBuilder 包含了一条链式 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();
}

这种风格还支持更多的自定义 predicate 。RouteDefinitionLocator bean 定义的predicate 用 and 逻辑组合。使用 流式Java API,你可以使用 and(),or(),以及negate() 去操作 Predicate 类。

11.4 基于注册中心的路由定义定位器(DiscoveryClient Route Definition Locator)

可以基于 带 DiscoveryClient(服务发现客户端)的 服务注册中心 去配置网关创建路由。

要想启用这个功能,配置:spring.cloud.gateway.discovery.locator.enabled=true,并确保DiscoveryClient实现(例如 Netflix Eureka、Consul 或 Zookeeper)在类路径上并已启用。

相关源码

// 从配置文件中读取相关配置 从这个类我们可以知道 能为路由定义定位器配置些什么参数
@ConfigurationProperties("spring.cloud.gateway.discovery.locator")
public class DiscoveryLocatorProperties {

	/** 是否开启网关集成DiscoverClient. */
	private boolean enabled = false;

	/**
	 * routeId前缀, 默认取 discoveryClient.getClass().getSimpleName() + “_” + serviceId
	 */
	private String routeIdPrefix;

	/**
	 * 判断是否包含SpEL表达 默认: true.
	 */
	private String includeExpression = "true";

	/**
	 * 路由的url的SpEl表达式, 默认为: 'lb://' + serviceId.: 
	 */
	private String urlExpression = "'lb://'+serviceId";

	/**
     * 是否在 predicate 和 filter 中将 serviceid 转成小写, 默认 false.
	 */
	private boolean lowerCaseServiceId = false;
	
	
	private List<PredicateDefinition> predicates = new ArrayList<>();

	private List<FilterDefinition> filters = new ArrayList<>();

11.4.1 为 DiscoverClient 配置 predicates 和 filters

默认情况下,网关会给 带服务发现的路由 配置一个predicate 和 filter
默认的 predicate 是 一个 路径(path)断言,定义的模式为 /serviceId/** ,serviceId是服务注册中心提供的 服务id
默认的 filter 是 重写路径过滤器(rewrite path filter),正则参数为: /serviceId/?( ?< remaining >. * ) ,replacement :/${remaining}。在向下游传递请求时会去除 serviceId。
自定义 predicate 和 filter :配置

  • spring.cloud.gateway.discovery.locator.predicates[x]
  • spring.cloud.gateway.discovery.locator.filters[y]
    这样做的时候,要确保自己定义的要包含默认的 predicate 和 filter
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}'"

12、Reactor Netty 访问日志

要启用 Reactor Netty 访问日志,设置 -Dreactor.netty.http.server.accessLogEnabled=true.
可以将日志记录系统配置为单独的访问日志文件。以下示例创建一个 Logback 配置:

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>

13、CORS(跨源资源共享) 配置

可以通过配置网关来控制 CORS 的行为,“全局” CORS 配置是 URL 模式到Spring FrameworkCorsConfiguration的映射。

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "https://docs.spring.io"
            allowedMethods:
            - GET

允许来自 docs.spring.io 的所有 GET 请求路径的请求发出 CORS 请求。
如果想给 那些没有被 网关路由管理的请求 配置 cors ,设置spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping为true
。这样设置支持预请求。关于CORS,可以参考文章 跨源资源共享

14、执行器 API

网关执行器 让你能够监控网关应用程序并与其交互。要实现远程交互,需要在配置文件中配置 确认开启并对外暴露 可以通过 HTTP或JMX访问的节点

management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway

14.1 详细的执行器格式

spring cloud gateway 加入了更多的关于每个路由的细节,这时的你可以查看每个路由关联的 predicate 和 filter 以及任意的的可用配置。
/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

14.2 检索路由过滤器

本节介绍了如何检索路由过滤器,包括

  • 全局过滤器(global filters)
  • 网关路由过滤器(gateway ruote-filters)

14.2.1 全局过滤器

要检索全局过滤器,发送一个 GET 请求到 /actuator/gateway/globalfilters,会收到如下响应:

{
  "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
}

这里面包含了已生效的 全局过滤器的细节信息。每一个全局过滤器,都是一个过滤器对象的字符串表示。

14.2.2 路由过滤器

检索路由过滤器 ,发送 GET 请求到 /actuator/gateway/routefilters 。结果如下:

{
  "[AddRequestHeaderGatewayFilterFactory@570ed9c configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
  "[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]": null,
  "[SaveSessionGatewayFilterFactory@4449b273 configClass = Object]": null
}

响应包含了全部的路由过滤器的详细信息。

14.3 刷新路由缓存

发送一个 POST 请求到 /actuator/gateway/refresh。请求返回一个没有响应体的200的响应。

14.4 检索网关中定义的路由

想要检索网关中定义的路由,发送一个 GET 请求到 /actuator/gateway/routes 。响应如下

[{
  "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
}]

响应中包含了网关中定义的全部路由的详细信息。下列表格中阐述了上门结构中每一个元素的含义

PathTypeDescription
route_idString路由 ID
route_object.predicateObjict路由 断言
route_object.filtersArray网关路由过滤器
orderNumber路由执行顺序

14.5 检索特定路由的信息

想要检索某个指定的路由的信息, 发送一个 GET 请求到 /actuator/gateway/routes/{id}
响应如下

{
  "id": "first_route",
  "predicates": [{
    "name": "Path",
    "args": {"_genkey_0":"/first"}
  }],
  "filters": [],
  "uri": "https://www.uri-destination.org",
  "order": 0
}

14.6 创建和删除特定路由

创建一个路由,发送一个带json body(集体格式参照14.5) POST 请求到 /gateway/routes/{id_route_to_create}

删除一个特定路由,发送一个 DELETE 请求到 /gateway/routes/{id_route_to_delete}

15 故障排除

本节介绍在使用 spring cloud gateway 时可能出现的问题。没有什么太多内容。故不作详细介绍,具体参见官方文档。

16、开发者指南

自定义 网关组件

16.1 自定义路由 predicate

自定义一个 predicate ,你需要实现 RoutePredicateFactory 。可以通过继承 AbstractRoutePredicateFactory 这个类来实现。

MyRoutePredicateFactory.java

public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {

    public MyRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        // 从Config对象获取配置t
        return exchange -> {
            // 获取请求
            ServerHttpRequest request = exchange.getRequest();
            // 从请求中获取信息 看看它是否与配置匹配
            return matches(config, request);
        };
    }

    public static class Config {
        //将配置属性放在这里
    }

}

16.2 自定义网关过滤器

自定义一个 GatewayFilter ,你需要实现 GatewayFilterFactory 。可以通过继承 AbstractGatewayFilterFactory 这个类来实现。

PreGatewayFilterFactory.java:

public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {

    public PreGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // 获取Config对象
        return (exchange, chain) -> {
            // 如果你想构建一个 “预执行(pre)”的filter,那么你需要在请求调用过滤链之前操作请求
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
            //使用 builder 来操作 request
            return chain.filter(exchange.mutate().request(builder.build()).build());
        };
    }

    public static class Config {
        //放置你的过滤链配置信息在这里
    }

}

PostGatewayFilterFactory.java

public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {

    public PostGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                // 以某种方式操作响应
            }));
        };
    }

    public static class Config {
    
    }

}

GatewayFilterFactory

16.2.1 自定义 filter 的命名以及在配置文件中的引用

自定义的 filter 的命名应该以 GatewayFilterFactory 结尾
例如,在配置文件中引用 名为 Something 的filter 时,这个filter 的类名应该是 SomethingGatewayFilterFactory 。

warning:不使用这个命名规范也是可以的,比如说你创建了一个AnotherThing的类,在配置文件中 使用 AnotherThing 引用也是可以的。但是这不是一个受支持的命名约定,在未来的版本中可能会被删除,最好是遵循上述命名规范

16.3 编写自定义全局过滤器

要编写自定义全局过滤器,您必须实现GlobalFilter接口。这将过滤器应用于所有请求。
以下示例分别显示了如何设置全局前置过滤器和后置过滤器:

@Bean
public GlobalFilter customGlobalFilter() {
    return (exchange, chain) -> exchange.getPrincipal()
        .map(Principal::getName)
        .defaultIfEmpty("Default User")
        .map(userName -> {
          //向代理请求添加头信息
          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 -> {
          //给响应添加头信息
          serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
              HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked": "It did not work");
          return serverWebExchange;
        })
        .then();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值