原文:Spring Cloud Gateway Reference Doc.

Spring Cloud Gateway 构建于Spring生态之上,包含 Spring 5, Spring Boot 2 和 Project Reactor.

Spring Cloud Gateway立志于提供一个简单而有效的方式来做api路由,拼提供灵活的切入点,用于提供如:安全、监控等。

1. 如何引入Spring Cloud Gateway

要在项目中包含Spring Cloud Gateway,请使用组 org.springframework.cloud和工件id spring-cloud-starter-gateway

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

请参阅Spring Cloud Project页面,以获取有关使用当前Spring Cloud Release Train设置构建系统的详细信息。

如果你项目中包含了Spring Cloud Gateway这个starter,但你由于业务需要,不想启用路由功能,可以通过配置spring.cloud.gateway.enabled=false来禁用。

Spring Cloud Gateway 需要由 Spring Boot 和 Spring Webflux 提供的 Netty 支持。 也这意味着该工程不能在传统的Servlet Container(tomcat,jetty等)中运行,也就是说不能打成一个WAR包运行。

2.术语

  • Route(路由): ,是网关的基本构建模块。它由一个ID,一个目标URI,一组断言和一组过滤器的集合定义。如果聚合谓词为真,则路由匹配。
  • Predicate(谓词):这是一个Java 8函数谓词。输入类型是一个Spring框架的ServerWebExchange。这允许开发人员匹配来自HTTP请求的任何内容,例如 headers 或 参数
  • Filter(过滤器):这是由Spring Framework GatewayFilter 由特定的工厂类构建的一系统过滤器,通过过滤器,我们可以添加或修改请求和返回值

3.如何工作

spring_cloud_gateway_diagram

由客户端发送请求到Spring Cloud Gateway. 如果网关Gateway Handler Mapping匹配到了对应的路由规则,则转交Gateway Web Handler处理。Gateway Web Handler通过 filter chain来处理请求. Filter包含前置Filter和后置Filter,前置Filter在代理请求发起之前执行,后置Filter在代理完成之后执行。类似于Spirng MVC中的Interceptor。

URIs 中 80 和 443 分别为 http 和 https 的默认端口,这两个端口可以默认省略,其它端口在路由的时候,需要显示申明。

4. 路由谓词工厂

Spring Cloud Gateway 的路由匹配是 Spring WebFlux HandlerMapping 的基础组件之一。 Spring Cloud Gateway 内置了一批路由谓词工厂,不同的路由谓词判断器,可以完成不同的路由匹配判断,多个谓词判断器可以通过and组合使用。

4.1 请求时间After谓词器

接受请求时间在指定日间之后的请求,比如新功能定时上线。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]

该路由接受 在 Jan 20, 2017 17:42 Mountain Time (Denver) 之后的任意请求,在该时间之前则匹配不到路由。

4.2 请求时间Before谓词器

接受请求时间在指定日间之前的请求,比如老功能定时下线,或指定业务限制服务时间。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]

该路由接受 在 Jan 20, 2017 17:42 Mountain Time (Denver) 之前的任意请求,在该时间之后则匹配不到该路由。

4.3 请求时间Between谓词器

接受请求时间在指定日间之内的请求,比如定业务限制服务时间,做限时活动。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

该路由接受 在 Jan 20, 2017 17:42 Mountain Time (Denver) 后并且在 Jan 21, 2017 17:42 Mountain Time (Denver) 之前的任意请求,不在该时间范围内的请求则匹配不到该路由。

4.4 Cookie谓词器

按正则表达式匹配是否有对应的cookie值

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Cookie=chocolate, ch.p

该路由接受包含chocolate这个cookie,并且cookie的值需要匹配ch.p这个正则表达式

正则表达式在线验证器

4.5 Header谓词器

按正则表达式匹配是否有对应的Header值

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Header=X-Request-Id, \d+

该路由接受包含X-Request-IdHeader的请求,并且值需要匹配\d+这个正则表达式

4.6 Host谓词器

Ant Path Matcher判断访问的Host是否匹配

多用于同一个网关接受来自不同的应用的访问请求,然后转发到对应的内部服务

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Host=**.somehost.org

该路由接受请求的Host匹配任意以 somehost.org 结尾的域名,比如 www.somehost.orgbeta.somehost.org

端口属于Host的一部分,除 80 和 443外,其它都不能忽略

4.7 HttpMethod谓词器

匹配是否满足指定的Http Method

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://example.org
predicates:
- Method=GET

只接受GET请求

4.8 Path(路径)谓词器

匹配请求的PATH(host后面的部分,不包括参数)是否满足指定的Path,匹配器采用的PathMatcher,本质上还是一个Ant Path Matcher

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Path=/foo/{segment}

比如该路由匹配 /foo/1/foo/bar,并且匹配器还会把匹配到的值放到segment这个临时变量中,该变通可以通过ServerWebExchange.getAttributes()按该keysegment取值

4.9 Query(参数)谓词器

验证参数是否存在,也可以附加按正则表达式验证值的类型是否合法

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=baz

要求必须携带baz参数

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=foo, ba.

要求必须携带foo参数,并且参数的值需要匹配 ba. 正则表达式,比如barbsz

4.10 RemoteAddr(客户端ip)谓词器

客户端访问ip必须在指定的ip范围内,该请求才被会路由。

比如192.168.0.1/16, 192.168.0.1为ip, 16为子网掩码

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://example.org
predicates:
- RemoteAddr=192.168.1.1/24

多个ip或网段,用,逗号连接

在线网络计算器

4.10.1 修改RemoteAddr(客户端ip)的解析方式

如果网关在代理的后端,则网关从Header头中获取的 X-Forwarded-Forip 也许并不是客户端真实的ip地址。

我们可以自定义实现接口RemoteAddressResolver以完成客户端ip的解析,在Spring Cloud Gateway中,提供了一个针对X-Forwarded-For的默认解析器XForwardedRemoteAddressResolver

XForwardedRemoteAddressResolver有两个构造方法

  • XForwardedRemoteAddressResolver::trustAll 永远只接收获取到从X-Forwarded-For获取到的第一个ip值,这种方式风险较高,比如客户端主动上传一个X-Forwarded-For的header上来,就可以模拟访问ip
  • XForwardedRemoteAddressResolver::maxTrustedIndex 绑定代理的层数,以nginx为例,比如Spring Cloud Gateway前面只代理了一层nginx,则该值就需要设置为1,如果nginx之前还有一层代理,则需要设置为2,以此类推,这个需要根据实际情况设置。

比如以X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3为例,则有以下结果

maxTrustedIndex 结果
[Integer.MIN_VALUE,0] (invalid, IllegalArgumentException during initialization)
1 0.0.0.3
2 0.0.0.2
3 0.0.0.1
[4, Integer.MAX_VALUE] 0.0.0.1

java代码

1
2
3
4
5
6
7
8
9
10
11
12
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. 路由过滤器工厂

路由过滤器能够以某种方式允许修改进入的Http请求或输出的对外的Http响应。

Spring Cloud Gateway包含许多内置的GatewayFilter工厂。

5.1 添加 RequestHeader Filter

可以向请求中添加指定的Header

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar

该配置表示会向该路由中的下游请求中添加X-Request-Foo:Bar这个header

5.2 添加 RequestParameter Filter

可以向请求中添加指定的Parameter

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: http://example.org
filters:
- AddRequestParameter=foo, bar

该配置表示会向该路由中的下游请求中添加foo=bar这个参数

5.3 添加 ResponseHeader Filter

可以向响应中添加指定的Header

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddResponseHeader=X-Response-Foo, Bar

该配置表示会向该路由的响应中添加X-Response-Foo:Bar这个Header

5.4 Hystrix Filter

Hystrix 由 Netflix 开源的一种断路器模式的实现。Hystrix过滤器允许你在路由上启用熔断器,避免级联故障(雪崩效应),或实现降级服务。

如果要使用Hystrix的熔断服务,需要加入依赖spring-cloud-starter-netflix-hystrix

熔断过滤器工厂只接收一个参数HystrixCommand

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: http://example.org
filters:
- Hystrix=myCommandName

以上配置表示, Hystrix过滤器的HystrixCommandmyCommandName

当然,Hystrix过滤器也接收一个可选参数fallbackUri,当前版本只支持forward:, 目前只支持内部URI匹配,如果进入降级,则会将当前请求转发到forward:之后内容匹配的内部controller方法,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: lb://backing-service:8088
predicates:
- Path=/consumingserviceendpoint
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/incaseoffailureusethis
- RewritePath=/consumingserviceendpoint, /backingserviceendpoint

就表明,如果发生熔断降级,则将请求发送到/incaseoffailureusethisURI匹配的controller方法。

本配置中还包含了Spring Cloud Netflix Ribbon负载均衡的示例,URI前缀关键字lb

Hystrix的更多配置可以参考Hystrix WIKI

比如超时时间要设置为5秒钟,则可以配置全局变量hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000

5.5 PrefixPath(路径前缀) Filter

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://example.org
filters:
- PrefixPath=/mypath

这配置表示,会在所有请求的路径之前添加 /mypath,比如请求的路径为/hello,则会修改为/mypath/hello

5.6 PreserveHostHeader(保留请求Host Header)Filter

是否向下传递原始 Host

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: http://example.org
filters:
- PreserveHostHeader

5.7 RequestRateLimiter(请求限流) GatewayFilter Factory

RequestRateLimiter工厂使用RateLimiter的实现去判断当前请求是否被接受,如果不被接受,则默认返回HTTP 429 - Too Many Requests

当前Filter支持可选参数keyResolver去自定义实现请求的唯一值判断标准。keyResolver是一个接口,需要自定义实现,在实现中定义Bean的时候,指定名称,如myKeyResolver,则可以在参数keyResolver的值中,通过SpEL表达式指定#{@myKeyResolver}于该Bean。

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

KeyResolver是允许可插拔的,未来会有更多的实现支持。

当前KeyResolver默认实现是通过PrincipalNameKeyResolverServerWebExchange获取Principal.getName()来实现的。

5.7.1 Redis RateLimiter (通过Redis限流)

Redis实现依赖于spring-boot-starter-data-redis-reactive,需要在pom.xml中引入该依赖。

算法使用的是 Token Bucket算法

参数redis-rate-limiter.replenishRate配置允许同一个用户每秒允许的请求次数。

参数redis-rate-limiter.burstCapacity配置同一秒总共接收多少个用户同时访问。如果设置为0则禁止所有访问。

burstCapacity的优先级高于replenishRate,如果超出限制,请求会得到HTTP 429 - Too Many Requests响应。

比如

application.yml

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: http://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20

Config.java

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

该配置表示每秒钟只允许20独立用户访问,并且每个用户在同一秒钟内最多可以发起10次请求。

KeyResolver定义了用户的判断标准是从请求参数user中获取。(当前这是很简单的做法,生产环境不建议这样处理。)

当然我们也可以实现RateLimiter接口来实现自己的限流器,配置方式和KeyResolver类似,也支持SpEL表达式。比如我们定义的Bean名称为myRateLimiter,则可以通过#{@myRateLimiter}来完成配置,示例配置如下:

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: http://example.org
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@myRateLimiter}"
key-resolver: "#{@userKeyResolver}"

5.8 RedirectTo(重定向) GatewayFilter Factory

重定向过滤器工厂接收状态码和url这两个参数。

  • 状态码必须是30x系列的重定向http code,比如301,302;
  • url也必须是一个有效的url
1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://example.org
filters:
- RedirectTo=302, http://acme.org

上面配置会向客户端发送一个http code为302,并且地址为http://acme.org 的header请求

5.9 RemoveNonProxyHeaders GatewayFilter Factory

RemoveNonProxyHeaders GatewayFilter Factory 会交指定的headers从代理中移除。

默认情况下会移除 IETF 规则的所有headers, 如下:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade

可以通过设置配置spring.cloud.gateway.filter.remove-non-proxy-headers.headers来指定要移除的header列表

5.10 RemoveRequestHeader(移除请求Header) GatewayFilter Factory

工厂类接收一个参数,指定要移除的header的名字

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: http://example.org
filters:
- RemoveRequestHeader=X-Request-Foo

比如该配置表示要移除X-Request-Foo这个header,不会向下传递。

5.11 RemoveResponseHeader(移除响应Header) GatewayFilter Factory

工厂类接收一个参数,指定要移除的header的名字

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: http://example.org
filters:
- RemoveResponseHeader=X-Response-Foo

比如该配置表示要移除X-Response-Foo这个header,不会返回给请求客户端

5.12 RewritePath(重写请求地址) GatewayFilter Factory

工厂接收两个参数

  • 路径匹配正则表达式
  • 替换参数

整个匹配采用Java regular expressions来进行处理

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: http://example.org
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/(?<segment>.*), /$\{segment}

比如请求路径为/foo/bar, 在上面的配置中,则会向下替换成请求/bar这个路径

在YAML中 $\ 会被替换成 $,这是YAML语法的定义

5.13 SaveSession(会话管理) GatewayFilter Factory

SaveSession会在向后转发之前调用WebSession的实现强制保存用户session。

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: save_session
uri: http://example.org
predicates:
- Path=/foo/**
filters:
- SaveSession

如果整合了Spring Security,这个是用户安全详情可以向下传递的关键

5.14 SecureHeaders(响应安全头) GatewayFilter Factory

安全头信息工厂内置了一系列默认的安全头响应,具体的可以点我阅读

以下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
  • frame-options
  • content-type-options
  • referrer-policy
  • content-security-policy
  • download-options
  • permitted-cross-domain-policies

5.15 SetPath(改写请求路径) GatewayFilter Factory

SetPath工厂通过接收path参数,意在提供一个简单的方式来操作请求path,匹配模式和Spring MVC相同。

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: http://example.org
predicates:
- Path=/foo/{segment}
filters:
- SetPath=/{segment}

比如请求的path为/foo/bar,通过路径匹配最终命中到bar这段,所以按照SetPath参数进行替换,最终向下请求的path为/bar

5.16 SetResponseHeader(设置响应Header) GatewayFilter Factory

提供向客户端响应额外header的能力。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: http://example.org
filters:
- SetResponseHeader=X-Response-Foo, Bar

比如上面这个配置,会向请求客户端返回headerX-Response-Foo:Bar,不管下游服务是否有X-Response-Foo这个header返回。

5.17 SetStatus GatewayFilter Factory

提供向客户端响应额外http code的能力。

1
2
3
4
5
6
7
8
9
10
11
12
spring:
cloud:
gateway:
routes:
- id: setstatusstring_route
uri: http://example.org
filters:
- SetStatus=BAD_REQUEST
- id: setstatusint_route
uri: http://example.org
filters:
- SetStatus=401

比如这个配置就是向客户端返回http code 401,用身份认证未通过

5.18 StripPrefix(前置路径截断) GatewayFilter Factory

该Filter提供前置路径截断功能,按/分割,指定截断的层数。在向下流做代理转发的时候,会按指定的层/name/bar数,将请求的path截断,通过。

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: http://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2

比如按上面的配置,假设客户端发起的请求为http://nameservice/name/bar/foo,通过StripPrefixFilter则会将请求path截断2层,后端服务接收到的请求将变成http://nameservice/foo

5.19 Retry(重试) GatewayFilter Factory

该Filter提供重试机制,接收以下参数:

  • retries: the number of retries that should be attempted
  • statuses: the HTTP status codes that should be retried, represented using org.springframework.http.HttpStatus
  • methods: the HTTP methods that should be retried, represented using org.springframework.http.HttpMethod
  • series: the series of status codes to be retried, represented using org.springframework.http.HttpStatus.Series
1
2
3
4
5
6
7
8
9
10
11
12
13
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

当前不支持转发协议的URI

6. Global Filters(全局过滤器)

GlobalFilterGatewayFilter都提供过滤器功能,当我们实现以上接口并以Bean加载到Spring容器中时,它会自动作用到所有路由中。(在以后的大版本更新中可能会有所改动)

6.1 Combined Global Filter and GatewayFilter Ordering

当一个请求进来时,如果匹配到对应的路由规则,那么所有的GlobalFilterGatewayFilter都会参与到过滤链路中去, 过滤顺序则是按org.springframework.core.Ordered来进行排序,我们以以通过实现其实接口或加注解@Order来完成排序的指定,值越低越优先执行,所以自己根据情况控制filter的排序。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Bean
@Order(-1)
public GlobalFilter a() {
return (exchange, chain) -> {
log.info("first pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("third post filter");
}));
};
}

@Bean
@Order(0)
public GlobalFilter b() {
return (exchange, chain) -> {
log.info("second pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("second post filter");
}));
};
}

@Bean
@Order(1)
public GlobalFilter c() {
return (exchange, chain) -> {
log.info("third pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("first post filter");
}));
};
}

6.2 Forward Routing Filter

The ForwardRoutingFilter looks for a URI in the exchange attribute ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR. If the url has a forward scheme (ie forward:///localendpoint), it will use the Spring DispatcherHandler to handler the request. The path part of the request URL will be overridden with the path in the forward URL. The unmodified original url is appended to the list in the ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR attribute.

6.3 LoadBalancerClient Filter

The LoadBalancerClientFilter looks for a URI in the exchange attribute ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR. If the url has a lb scheme (ie lb://myservice), it will use the Spring Cloud LoadBalancerClient to resolve the name (myservice in the previous example) to an actual host and port and replace the URI in the same attribute. The unmodified original url is appended to the list in the ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR attribute. The filter will also look in the ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR attribute to see if it equals lb and then the same rules apply.

application.yml.

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**

6.4 Netty Routing Filter

The Netty Routing Filter runs if the url located in the ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR exchange attribute has a http or https scheme. It uses the Netty HttpClient to make the downstream proxy request. The response is put in the ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR exchange attribute for use in a later filter. (There is an experimental WebClientHttpRoutingFilter that performs the same function, but does not require netty)

6.5 Netty Write Response Filter

The NettyWriteResponseFilter runs if there is a Netty HttpClientResponse in the ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR exchange attribute. It is run after all other filters have completed and writes the proxy response back to the gateway client response. (There is an experimental WebClientWriteResponseFilter that performs the same function, but does not require netty)

6.6 RouteToRequestUrl Filter

The RouteToRequestUrlFilter runs if there is a Route object in the ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR exchange attribute. It creates a new URI, based off of the request URI, but updated with the URI attribute of the Route object. The new URI is placed in the ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR exchange attribute`.

If the URI has a scheme prefix, such as lb:ws://serviceid, the lb scheme is stripped from the URI and placed in the ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR for use later in the filter chain.

6.7 Websocket Routing Filter

The Websocket Routing Filter runs if the url located in the ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR exchange attribute has a ws or wss scheme. It uses the Spring Web Socket infrastructure to forward the Websocket request downstream.

Websockets may be load-balanced by prefixing the URI with lb, such as lb:ws://serviceid.

[Note]
If you are using SockJS as a fallback over normal http, you should configure a normal HTTP route as well as the Websocket Route.

application.yml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
# SockJS route
- id: websocket_sockjs_route
uri: http://localhost:3001
predicates:
- Path=/websocket/info/**
# Normwal Websocket route
- id: websocket_route
uri: ws://localhost:3001
predicates:
- Path=/websocket/**

6.8 Gateway Metrics Filter

To enable Gateway Metrics add spring-boot-starter-actuator as a project dependency. Then, by default, the Gateway Metrics Filter runs as long as the property spring.cloud.gateway.metrics.enabled is not set to false. This filter adds a timer metric named “gateway.requests” with the following tags:

routeId: The route id
routeUri: The URI that the API will be routed to
outcome: Outcome as classified by HttpStatus.Series
status: Http Status of the request returned to the client
These metrics are then available to be scraped from /actuator/metrics/gateway.requests and can be easily integated with Prometheus to create a Grafana dashboard.

[Note]
To enable the pometheus endpoint add micrometer-registry-prometheus as a project dependency.

6.9 Making An Exchange As Routed

After the Gateway has routed a ServerWebExchange it will mark that exchange as “routed” by adding gatewayAlreadyRouted to the exchange attributes. Once a request has been marked as routed, other routing filters will not route the request again, essentially skipping the filter. There are convenience methods that you can use to mark an exchange as routed or check if an exchange has already been routed.

ServerWebExchangeUtils.isAlreadyRouted takes a ServerWebExchange object and checks if it has been “routed”
ServerWebExchangeUtils.setAlreadyRouted takes a ServerWebExchange object and marks it as “routed”

7. TLS / SSL

The Gateway can listen for requests on https by following the usual Spring server configuration. Example:

application.yml.

1
2
3
4
5
6
7
server:
ssl:
enabled: true
key-alias: scg
key-store-password: scg1234
key-store: classpath:scg-keystore.p12
key-store-type: PKCS12

Gateway routes can be routed to both http and https backends. If routing to a https backend then the Gateway can be configured to trust all downstream certificates with the following configuration:

application.yml.

1
2
3
4
5
6
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true

Using an insecure trust manager is not suitable for production. For a production deployment the Gateway can be configured with a set of known certificates that it can trust with the follwing configuration:

application.yml.

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
httpclient:
ssl:
trustedX509Certificates:
- cert1.pem
- cert2.pem

8. Configuration

Configuration for Spring Cloud Gateway is driven by a collection of RouteDefinitionLocators.c

RouteDefinitionLocator.java.

public interface RouteDefinitionLocator {
​ Flux getRouteDefinitions();
}
By default, a PropertiesRouteDefinitionLocator loads properties using Spring Boot’s @ConfigurationProperties mechanism.

The configuration examples above all use a shortcut notation that uses positional arguments rather than named ones. The two examples below are equivalent:

application.yml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: setstatus_route
uri: http://example.org
filters:
- name: SetStatus
args:
status: 401
- id: setstatusshortcut_route
uri: http://example.org
filters:
- SetStatus=401

For some usages of the gateway, properties will be adequate, but some production use cases will benefit from loading configuration from an external source, such as a database. Future milestone versions will have RouteDefinitionLocator implementations based off of Spring Data Repositories such as: Redis, MongoDB and Cassandra.

8.1 Fluent Java Routes API

To allow for simple configuration in Java, there is a fluent API defined in the RouteLocatorBuilder bean.

GatewaySampleApplication.java.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 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")
​ )
​ .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")
​ )
​ .build();
}

This style also allows for more custom predicate assertions. The predicates defined by RouteDefinitionLocator beans are combined using logical and. By using the fluent Java API, you can use the and(), or() and negate() operators on the Predicate class.

8.2 DiscoveryClient Route Definition Locator

The Gateway can be configured to create routes based on services registered with a DiscoveryClient compatible service registry.

To enable this, set spring.cloud.gateway.discovery.locator.enabled=true and make sure a DiscoveryClient implementation is on the classpath and enabled (such as Netflix Eureka, Consul or Zookeeper).

9. CORS Configuration

The gateway can be configured to control CORS behavior. The “global” CORS configuration is a map of URL patterns to Spring Framework CorsConfiguration.

application.yml.

1
2
3
4
5
6
7
8
9
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "docs.spring.io"
allowedMethods:
- GET

In the example above, CORS requests will be allowed from requests that originate from docs.spring.io for all GET requested paths.

10. Actuator API

TODO: document the /gateway actuator endpoint

11. Developer Guide

TODO: overview of writing custom integrations

11.1 Writing Custom Route Predicate Factories

TODO: document writing Custom Route Predicate Factories

11.2 Writing Custom GatewayFilter Factories

In order to write a GatewayFilter you will need to implement GatewayFilterFactory. There is an abstract class called AbstractGatewayFilterFactory which you can extend.

PreGatewayFilterFactory.java.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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 change.filter
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
//use builder to manipulate the request
return chain.filter(exchange.mutate().request(request).build());
};
}

public static class Config {
//Put the configuration properties for your filter here
}

}
PostGatewayFilterFactory.java.

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
}

}

11.3 Writing Custom Global Filters

TODO: document writing Custom Global Filters

11.4 Writing Custom Route Locators and Writers

TODO: document writing Custom Route Locators and Writers

12. Building a Simple Gateway Using Spring MVC or Webflux

Spring Cloud Gateway provides a utility object called ProxyExchange which you can use inside a regular Spring web handler as a method parameter. It supports basic downstream HTTP exchanges via methods that mirror the HTTP verbs. With MVC it also supports forwarding to a local handler via the forward() method. To use the ProxyExchange just include the right module in your classpath (either spring-cloud-gateway-mvc or spring-cloud-gateway-webflux).

MVC example (proxying a request to “/test” downstream to a remote server):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@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();
}

}
The same thing with 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();
}

}
There are convenience methods on the ProxyExchange to enable the handler method to discover and enhance the URI path of the incoming request. For example you might want to extract the trailing elements of a path to pass them downstream:

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

All the features of Spring MVC or Webflux are available to Gateway handler methods. So you can inject request headers and query parameters, for instance, and you can constrain the incoming requests with declarations in the mapping annotation. See the documentation for @RequestMapping in Spring MVC for more details of those features.

Headers can be added to the downstream response using the header() methods on ProxyExchange.

You can also manipulate response headers (and anything else you like in the response) by adding a mapper to the get() etc. method. The mapper is a Function that takes the incoming ResponseEntity and converts it to an outgoing one.

First class support is provided for “sensitive” headers (“cookie” and “authorization” by default) which are not passed downstream, and for “proxy” headers (x-forwarded-*).




如果您觉得这篇文章对您有所帮助, 点我, 可以请我喝杯咖啡。
< 支付宝 | 微信 >
Published with Hexo and Theme by Kael
Flag Counter
X