SpringCloud网关(响应式-反应式)-文档参考

204 阅读11分钟

这是基于 webfluxapi网关,特点如下

  • 基于spring boot
  • 提供匹配请求中任意属性的路由
  • 路由上支持断言和过滤器
  • 断路整合
  • DiscoveryClient整合
  • 请求限流
  • 路径重写

引入

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

启用或者关闭网关功能

spring.cloud.gateway.enabled=false

术语

  • 路由 route
    • 由id、目标uri、断言集合和过滤器集合构成。断言为true时,则匹配该路由
  • 断言 predicate
    • java8的函数Predicate,入参为ServerWebExchange。可以判断请求中任何内容
  • 过滤器 filter
    • GatewayFilter实例,可以修改请求和响应

工作原理

断言工厂和过滤器工厂配置

方式:1.快捷方式 2.全参数

快捷方式配置

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

解释:使用了Cookie断言,当请求的Cookie参数mycookie的值等于mycookievalue时路由匹配,uri为 [https://example.org](https://example.org)

全参数配置

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

作用同上,全部参数配置方式

断言工厂

内置了很多断言,可以使用and进行断言组合

After

在指定日期/时间之后发生的请求

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

Before

在指定日期/时间之后发生的请求

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

Between

在指定日期/时间之间发生的请求。两个时间用逗号分隔

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

Cookie

给定名称且其值与正则表达式匹配的 Cookie

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

cookie参数chocolate的值等于ch.p

Header

匹配请求头的值符合正则

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

请求头X-Request-Id必须为全数字

Host

匹配请求头中的host,符合正则

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

请求host必须符合**.somehost.org*或者*.anotherhost.org

Method

请求方法匹配

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

Path

请求路径匹配

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

比如 /red/1 ,/red/1/ ,/red/blue /blue/1/ 会匹配上

如果设置 matchTrailingSlash=false/red/1/``/blue/1/ 不会匹配。

使用下面的方法可以获取匹配的 segment

Map<String, String> uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange);
String segment = uriVariables.get("segment");

Query

匹配query参数

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

请求中包含green查询参数则匹配,比如x?green=xxx

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

请求中包含red查询参数,并且值为 green``greex等会匹配。比如x?red=green

RemoteAddr

匹配客户端ip地址 ,符合CIDR模式

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

Weight

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

按照权重进行路由,大约 80%请求进入weighthigh。剩余20%进入weightlow

XForwardedRemoteAddr

匹配 <font style="color:rgb(187, 188, 190);">X-Forwarded-For</font>

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

过滤器工厂

内置过滤器

AddRequestHeader

添加请求头

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

下游请求中添加请求头X-Request-red: blue

使用捕获的变量

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

AddRequestHeadersIfNotPresent

请求头中不存在时添加

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

添加多值请求头

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_headers_route
        uri: https://example.org
        filters:
        - AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-1:green

也支持捕获变量

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

AddRequestParameter

添加请求参数

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

下游请求中增加参数red=blue

支持捕获变量

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

AddResponseHeader

添加响应头

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

支持捕获变量

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

CircuitBreaker

断路器支持,需要添加依赖

CircuitBreaker GatewayFilter Factory :: Spring Cloud Gateway

 <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
  </dependency>
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: https://example.org
        filters:
        - CircuitBreaker=myCircuitBreaker

支持 fallbackUri

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

等同代码

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

支持捕获变量

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}

路由跳转

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

支持状态码

spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingServiceEndpoint
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/inCaseOfFailureUseThis
            statusCodes:
              - 500
              - "NOT_FOUND"

等同代码

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint")
            .filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis").addStatusCode("INTERNAL_SERVER_ERROR"))
                .rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088")
        .build();
}

CacheRequestBody

请求体一般只能读取一次,许多场景中需要多次读取,使用此过滤器进行缓存

代码为

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("cache_request_body_route", r -> r.path("/downstream/**")
            .filters(f -> f.prefixPath("/httpbin")
        		.cacheRequestBody(String.class).uri(uri))
        .build();
}
spring:
  cloud:
    gateway:
      routes:
      - id: cache_request_body_route
        uri: lb://downstream
        predicates:
        - Path=/downstream/**
        filters:
        - name: CacheRequestBody
          args:
            bodyClass: java.lang.String

使用下面的代码可以获取

ServerWebExchange.getAttributes(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR)

DedupeResponseHeader

移除重复响应头

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

支持策略参数strategy 可选值为``RETAIN_FIRST默认 ,RETAIN_LAST,RETAIN_UNIQUE

FallbackHeaders

使用fallback回退时,允许携带上次请求的异常信息到请求头中

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

JsonToGrpc

将 JSON 负载转换为 gRPC 请求

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: HelloService
                method: hello

LocalResponseCache

需要

  1. 引入项目
  • com.github.ben-manes.caffeine:caffeine
  • spring-boot-starter-cache
  1. 开启本地缓存配置
spring.cloud.gateway.filter.local-response-cache.enabled

允许缓存响应正文和标头,条件如下

  • 只能缓存无主体的 GET 请求
  • 仅缓存以下状态代码之一的响应:HTTP 200 (OK)、HTTP 206 (Partial Content) 或 HTTP 301 (Moved Permanently)
  • 响应头 Cache-Control 为no-store 或 private时,不缓存

如果响应已缓存,并且新请求标头Cache-Control中具有 no-cache 值的,则它将返回 304 (Not Modified) 的无体响应

代码示例

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
            .filters(f -> f.prefixPath("/httpbin")
        		.localResponseCache(Duration.ofMinutes(30), "500MB")
            ).uri(uri))
        .build();
}
spring:
  cloud:
    gateway:
      routes:
      - id: resource
        uri: http://localhost:9000
        predicates:
        - Path=/resource
        filters:
        - LocalResponseCache=30m,500MB

MapRequestHeader

请求头替换

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

下游请求头中使用 X-Request-Red 替换了Blue

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

ModifyResponseBody

修改响应体(仅java可用

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

PrefixPath

添加前缀

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

给下游请求添加前缀。比如 /hello 变成 /mypath/hello

PreserveHostHeader

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

RedirectTo

重定向

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

This will send a status 302 with a Location:acme.org header to perform a redirect.

携带参数重定向

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

例如?skip=10,返回 302 with a Location [https://acme.org?skip=10](https://acme.org?skip=10)

RemoveJsonAttributesResponseBody

移除响应体json中的某些属性。可以移除根或者任意级别

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

This removes attributes "id" and "color" from the JSON content body at root level.

spring:
  cloud:
    gateway:
      routes:
      - id: removejsonattributes_recursively_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - RemoveJsonAttributesResponseBody=id,color,true

This removes attributes "id" and "color" from the JSON content body at any level.

RemoveRequestHeader

移除请求头

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

下游请求头中没有X-Request-Foo

RemoveRequestParameter

移除请求参数

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

RemoveResponseHeader

移除响应头

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

RequestHeaderSize

最大请求头尺寸

spring:
  cloud:
    gateway:
      routes:
      - id: requestheadersize_route
        uri: https://example.org
        filters:
        - RequestHeaderSize=1000B

This will send a status 431 if size of any request header is greater than 1000 Bytes.

RequestRateLimiter

请求限速。超限时返回HTTP 429 - Too Many Requests

spring:
  cloud:
    gateway:
      routes:
      - id: limit
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            key-resolver: "#{@userkeyresolver}"

key-resolver 接口如下

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

如果key-resolver 解析不到值,默认请求会被拒绝。可以通过参数调整

spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key=true

可以设置拒绝请求的状态码

spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code

Redis配置

Scaling your API with rate limiters

需要依赖

spring-boot-starter-data-redis-reactive

限流算法为token桶 en.wikipedia.org/wiki/Token_…

参数

redis-rate-limiter.replenishRate  每秒最大请求数(token填充的速率

redis-rate-limiter.burstCapacity 一秒内最大请求数(token的容量,0=拒绝所有请求

redis-rate-limiter.requestedTokens 每个请求使用的token

  • replenishRate 等于 burstCapacity 时,请求速率是稳定的
  • burstCapacity 大于 replenishRate 时,允许稍微过量的请求
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

key-resolver示例

@Bean
KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}

使用spel配置key-resolver

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            rate-limiter: "#{@myRateLimiter}"
            key-resolver: "#{@userKeyResolver}"

RewriteLocationResponseHeader

修改响应头中的Location

spring:
  cloud:
    gateway:
      routes:
      - id: rewritelocationresponseheader_route
        uri: http://example.org
        filters:
        - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,

RewritePath

路径重写

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

For a request path of /red/blue, this sets the path to /blue before making the downstream request. Note that the shouldbereplacedwithshould be replaced with\ because of the YAML specification.

RewriteRequestParameter

重写请求查询参数

spring:
  cloud:
    gateway:
      routes:
      - id: rewriterequestparameter_route
        uri: https://example.org
        predicates:
        - Path=/products
        filters:
        - RewriteRequestParameter=campaign,fall2023

For a request to /products?campaign=old, this sets the request parameter to campaign=fall2023.

RewriteResponseHeader

重写响应头

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

For a header value of /42?user=ford&password=omg!what&flag=true, it is set to /42?user=ford&password=***&flag=true after making the downstream request. You must use  tomean\ to mean because of the YAML specification.

SaveSession

强制调用 WebSession::save 方法,在转发请求前

spring:
  cloud:
    gateway:
      routes:
      - id: save_session
        uri: https://example.org
        predicates:
        - Path=/foo/**
        filters:
        - SaveSession

SecureHeaders

向响应中添加多个安全相关的头部

blog.appcanary.com/2017/http-s…

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=x-frame-options,strict-transport-security

SetPath

修改转发请求的路径

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

/red/blue -> /blue

SetRequestHeader

设置修改请求头(不是添加

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

if the downstream server responded with X-Request-Red:1234, it will be replaced with X-Request-Red:Blue,

支持模板变量

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

SetResponseHeader

设置修改响应头(不是添加

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

if the downstream server responded with X-Response-Red:1234, it will be replaced with X-Response-Red:Blue,

SetStatus

设置响应码

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

StripPrefix

移除指定数量的路径前缀

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

移除2个路径前缀

/name/blue/red -> nameservice/red``

Retry

重试

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 #应重试的 HTTP 状态代码
            methods: GET,POST #应重试的 HTTP 方法
            backoff:
              firstBackoff: 10ms #首次重试避让
              maxBackoff: 50ms #首次重试避让
              factor: 2 # 避让等待递增指数
              basedOnPreviousValue: false

简化写法

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

RequestSize

限制请求大小

spring:
  cloud:
    gateway:
      routes:
      - id: request_size_route
        uri: http://localhost:8080/upload
        predicates:
        - Path=/upload
        filters:
        - name: RequestSize
          args:
            maxSize: 5000000

SetRequestHostHeader

重写请求的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

TokenRelay

token中继

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


spring:
  cloud:
    gateway:
      routes:
      - id: resource
        uri: http://localhost:9000
        predicates:
        - Path=/resource
        filters:
        - TokenRelay=myregistrationid

设置默认过滤器

应用于所有路由的过滤器

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Red, Default-Blue
      - PrefixPath=/httpbin

全局过滤器

请求到达时,网关会把所有匹配的过滤器组成一个过滤器链,然后排序后依次调用。

  • 请求分为pre 和post阶段。

示例

@Bean
public GlobalFilter customFilter() {
    return new CustomGlobalFilter();
}

public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

指标 metrics

management:
  endpoints:
    web:
      exposure:
        include: "*"

spring:
  cloud:
    gateway:
      enabled: true
      metrics:
        enabled: true
        tags:
          path:
            enabled: true
http://localhost:8080/actuator/metrics/spring.cloud.gateway.requests

{
    "name": "spring.cloud.gateway.requests",
    "baseUnit": "seconds",
    "measurements": [
        {
            "statistic": "COUNT",
            "value": 22
        },
        {
            "statistic": "TOTAL_TIME",
            "value": 9.6251798
        },
        {
            "statistic": "MAX",
            "value": 0
        }
    ],
    "availableTags": [
        {
            "tag": "routeUri",
            "values": [
                "http://httpbin.org:80"
            ]
        },
        {
            "tag": "path",
            "values": [
                "/*"
            ]
        },
        {
            "tag": "path.enabled",
            "values": [
                "true"
            ]
        },
        {
            "tag": "routeId",
            "values": [
                "path_route"
            ]
        },
        {
            "tag": "httpMethod",
            "values": [
                "GET"
            ]
        },
        {
            "tag": "outcome",
            "values": [
                "SUCCESSFUL"
            ]
        },
        {
            "tag": "status",
            "values": [
                "OK"
            ]
        },
        {
            "tag": "httpStatusCode",
            "values": [
                "200"
            ]
        }
    ]
}

Forward路由过滤器

当请求的uri为 <font style="color:rgb(187, 188, 190);">forward:///localendpoint</font> 这样时,内部使用Spring的DispathcerHandler来处理请求

Netty路由过滤器

用于处理 http 和 https 请求

  • 默认Netty使用HttpClient来请求下游服务器
  • 还有一个不依赖Netty的WebClientHttpRoutingFilter 同样的功能

Netty响应过滤器

  • 用NettyWriteResponseFilter 将响应写回客户端
  • 还有一个WebClientWriteResponseFilter 同样的功能

ReactiveLoadBalancerClientFilter

用于处理 lb://myserviceschema的请求

  • 使用Spring Cloud的ReactorLoadBalancer 来解析 myservice的真实地址

配置文件中使用方式为

spring:
  cloud:
    gateway:
      routes:
      - id: myRoute
        uri: lb://service
        predicates:
        - Path=/service/**

Websocket路由过滤器

处理 ws 或者 wss 请求

支持负载均衡,比如lb:ws://serviceid形式的服务定义

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/**

请求头过滤器

Forwarded过滤器

在请求中添加一个Forwarded头,

RemoveHopByHop 过滤器

移除请求中的下面头部

"connection", "keep-alive", "transfer-encoding", "te", "trailer", "proxy-authorization",
       "proxy-authenticate", "x-application-context", "upgrade"

XForwarded 过滤器

用于处理 X-Forwarded-* 之类的头

开启配置如下

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

TLS SSL配置(一般不需要

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

下游https不可信任(使用自签证书)时,可以强行使用

spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          useInsecureTrustManager: true

配置信任证书

spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          trustedX509Certificates:
          - cert1.pem
          - cert2.pem

tls握手配置

网关使用了client池子,用于优化性能

spring: 
  cloud:
    gateway:
      httpclient:
        ssl:
          handshake-timeout-millis: 10000
          close-notify-flush-timeout-millis: 3000
          close-notify-read-timeout-millis: 0

配置

配置的核心类是 RouteDefinitionLocator

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

默认使用 <font style="color:#080808;background-color:#ffffff;">PropertiesRouteDefinitionLocator</font>从配置文件中加载路由

路由元数据

给路由配置额外数据

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);
// get all metadata properties
route.getMetadata();
// get a single metadata property
route.getMetadata(someKey);

超时配置

全局超时

spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000 # 毫秒,代理转发建立连接的时间
        response-timeout: 5s  # Duration格式  代理转发下游响应时间

单个路由超时

      - id: per_route_timeouts
        uri: https://example.org
        predicates:
          - name: Path
            args:
              pattern: /delay/{timeout}
        metadata:
          response-timeout: 200
          connect-timeout: 200

代码中配置

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

永不超时

配置负数表示永不超时

      - id: per_route_timeouts
        uri: https://example.org
        predicates:
          - name: Path
            args:
              pattern: /delay/{timeout}
        metadata:
          response-timeout: -1

路由流式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();
}

服务发现路由

使用DiscoveryClient从服务注册中心获取路由。

路由的默认服务格式为 lb://service-name

需要引入依赖,支持从Netflix、Eureka、Consul、Zookeeper、Kubernetes中发现(相关jar必须在)

org.springframework.cloud:spring-cloud-starter-loadbalancer

开启自动路由发现

spring.cloud.gateway.discovery.locator.enabled=true

给DiscoveryClient配置断言或过滤器

默认情况

  • 路由使用 /serviceId/** 的路径断言
  • 路由使用 正则 /serviceId/?(?<remaining>.*)替换成 /${remaining}。serviceId前缀在转发到下游时会被移除

自定义断言和过滤器方式如下

spring:
  cloud:
    gateway:
      discovery:
        locator:
          predicates:
          - name: Host
            args:
              pattern: "'**.foo.com'"
          - name: Path
            args:
              pattern: "'/'+serviceId+'/**'"
          filters:
          - name: CircuitBreaker
            args:
              name: serviceId
          - name: RewritePath
            args:
              regexp: "'/' + serviceId + '/?(?<remaining>.*)'"
              replacement: "'/${remaining}'"

Reactor netty 访问日志

设置启动参数

-Dreactor.netty.http.server.accessLogEnabled=true

日志文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 输出的格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}: %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="STDOUT"/>
    </appender>

    <logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
        <appender-ref ref="async"/>
    </logger>
    <root level="debug">
        <appender-ref ref="STDOUT"/>
    </root>

</configuration>

跨域配置

全局跨域配置

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

支持预见preflight请求

spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping=true

单路由跨域配置

spring:
  cloud:
    gateway:
      routes:
      - id: cors_route
        uri: https://example.org
        predicates:
        - Path=/service/**
        metadata:
          cors:
            allowedOrigins: '*'
            allowedMethods:
              - GET
              - POST
            allowedHeaders: '*'
            maxAge: 30

Actuator

端点为 /gateway

开启

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

包含的端口路径

[
  {
    "href":"/actuator/gateway/",
    "methods":[ "GET" ]
  },
  {
    "href":"/actuator/gateway/routedefinitions",
    "methods":[ "GET" ]
  },
  {
    "href":"/actuator/gateway/globalfilters",
    "methods":[ "GET" ]
  },
  {
    "href":"/actuator/gateway/routefilters",
    "methods":[ "GET" ]
  },
  {
    "href":"/actuator/gateway/routes",
    "methods":[ "POST", "GET" ]
  },
  {
    "href":"/actuator/gateway/routepredicates",
    "methods":[ "GET" ]
  },
  {
    "href":"/actuator/gateway/refresh",
    "methods":[ "POST" ]
  },
  {
    "href":"/actuator/gateway/routes/route-id-1/combinedfilters",
    "methods":[ "GET" ]
  },
  {
    "href":"/actuator/gateway/routes/route-id-1",
    "methods":[ "POST", "DELETE", "GET" ]
  }
]

开启路由定义细节

spring.cloud.gateway.actuator.verbose.enabled=false

细节如下

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

刷新路由缓存

POST /actuator/gateway/refresh

部分刷新

POST  /actuator/gateway/refresh?metadata=group:group-1

创建和删除路由

POST  /gateway/routes/{id_route_to_create}
# 路由定义结构体
{
  "id": "first_route",
  "predicates": [{
    "name": "Path",
    "args": {"_genkey_0":"/first"}
  }],
  "filters": [],
  "uri": "https://www.uri-destination.org",
  "order": 0
}

DELETE  /gateway/routes/{id_route_to_delete}

多网关实例共享路由

路由存储在 RouteDefinitionRepository 中,默认实现为 InMemoryRouteDefinitionRepository

多个网关需要共享路由时,使用类 RedisRouteDefinitionRepository

依赖

spring-boot-starter-data-redis-reactive

开启配置

 spring.cloud.gateway.redis-route-definition-repository.enabled=true

问题排查

日志 debug 或 trace

下面的package

org.springframework.cloud.gateway

org.springframework.http.server.reactive

org.springframework.web.reactive

org.springframework.boot.autoconfigure.web

reactor.netty

redisratelimiter

监听

Reactor Netty HttpClient and HttpServer 支持监听

开启

  • reactor.netty 日志设为DEBUG或Trace
  • 配置 spring.cloud.gateway.httpserver.wiretap=true
  • 配置 spring.cloud.gateway.httpclient.wiretap=true
spring:
  cloud:
    gateway:
      enabled: true
      httpclient:
        wiretap: true
      httpserver:
        wiretap: true

请求监听的内容

转发的请求

响应

开发指引

自定义断言

命名 必须以 PredicateFactory 为结尾

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

}

自定义过滤器

命名必须以 GatewayFilterFactory 为后缀

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

}

自定义全局过滤器

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