目录
1. How to Include Spring Cloud Gateway
2. Glossary
3. How It Works
4. Route Predicate Factories
5. GatewayFilter Factories
6. Global Filters
7. TLS / SSL
8. Configuration
9. Reactor Netty Access Logs
10. CORS Configuration
11. Actuator API
12. Developer Guide
该项目提供了一个建立在Spring Ecosystem之上的API网关,包括:Spring 5,Spring Boot 2和Project Reactor。Spring Cloud Gateway旨在提供一种简单而有效的方式来对API进行路由,并为他们提供切面,例如:安全性,监控/指标 和弹性等。
要在项目中引入Spring Cloud Gateway,需要引用 group org.springframework.cloud
和 artifact id为spring-cloud-starter-gateway
starter。最新的Spring Cloud Release 构建信息,请参阅Spring Cloud Project page。
如果应用了该starter,但由于某种原因不希望启用网关,请进行设置spring.cloud.gateway.enabled=false
。
重要
Spring Cloud Gateway依赖Spring Boot和Spring Webflux提供的Netty runtime。它不能在传统的Servlet容器中工作或构建为WAR
ServerWebExchange
。这允许开发人员可以匹配来自HTTP请求的任何内容,例如Header或参数。GatewayFilter
实例。所以可以在返回请求之前或之后修改请求和响应的内容。客户端向Spring Cloud Gateway发出请求。如果Gateway Handler Mapping确定请求与路由匹配,则将其发送到Gateway Web Handler。此handler通过特定于该请求的过滤器链处理请求。图中filters被虚线划分的原因是filters可以在发送代理请求之前或之后执行逻辑。先执行所有“pre filter”逻辑,然后进行请求代理。在请求代理执行完后,执行“post filter”逻辑。
注意
HTTP和HTTPS URI默认端口设置是80和443。
Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping
基础结构的一部分进行匹配。Spring Cloud Gateway包含许多内置的路由断言Factories。这些断言都匹配HTTP请求的不同属性。多个路由断言Factories可以通过 and
组合使用。
After Route Predicate Factory采用一个参数——日期时间。在该日期时间之后发生的请求都将被匹配。
application.yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
Before Route Predicate Factory采用一个参数——日期时间。在该日期时间之前发生的请求都将被匹配。
application.yml.
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
Between 路由断言 Factory有两个参数,datetime1和datetime2。在datetime1和datetime2之间的请求将被匹配。datetime2参数的实际时间必须在datetime1之后。
application.yml.
spring:
cloud:
gateway:
routes:
- id: between_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]
Cookie 路由断言 Factory有两个参数,cookie名称和正则表达式。请求包含次cookie名称且正则表达式为真的将会被匹配。
application.yml
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Cookie=chocolate, ch.p
Header 路由断言 Factory有两个参数,header名称和正则表达式。请求包含次header名称且正则表达式为真的将会被匹配。
application.yml.
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://example.org
predicates:
- Header=X-Request-Id, \d+
Host 路由断言 Factory包括一个参数:host name列表。使用Ant路径匹配规则,.
作为分隔符。
application.yml.
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
Method 路由断言 Factory只包含一个参数: 需要匹配的HTTP请求方式
application.yml.
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://example.org
predicates:
- Method=GET
所有GET请求都将被路由
Path 路由断言 Factory 有2个参数: 一个Spring PathMatcher
表达式列表和可选matchOptionalTrailingSeparator
标识 .
application.yml.
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Path=/foo/{segment},/bar/{segment}
例如: /foo/1
or /foo/bar
or /bar/baz
的请求都将被匹配
URI 模板变量 (如上例中的 segment
) 将以Map的方式保存于ServerWebExchange.getAttributes()
key为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
. 这些值将在GatewayFilter Factories使用
可以使用以下方法来更方便地访问这些变量。
Map uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);
String segment = uriVariables.get("segment");
Query 路由断言 Factory 有2个参数: 必选项 param
和可选项 regexp
.
application.yml.
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=baz
则包含了请求参数 baz
的都将被匹配。
application.yml.
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=foo, ba.
如果请求参数里包含foo
参数,并且值匹配为ba.
表达式,则将会被路由,如:bar
and baz
RemoteAddr 路由断言 Factory的参数为 一个CIDR符号(IPv4或IPv6)字符串的列表,最小值为1,例如192.168.0.1/16(其中192.168.0.1是IP地址并且16是子网掩码)。
application.yml.
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://example.org
predicates:
- RemoteAddr=192.168.1.1/24
如果请求的remote address 为 192.168.1.10
则将被路由
默认情况下,RemoteAddr 路由断言 Factory使用传入请求中的remote address。如果Spring Cloud Gateway位于代理层后面,则可能与实际客户端IP地址不匹配。
可以通过设置自定义RemoteAddressResolver
来自定义解析远程地址的方式。Spring Cloud Gateway网关附带一个非默认远程地址解析程序,它基于X-Forwarded-For header, XForwardedRemoteAddressResolver
.
XForwardedRemoteAddressResolver
有两个静态构造函数方法,采用不同的安全方法:
XForwardedRemoteAddressResolver::TrustAll
返回一个RemoteAddressResolver
,它始终采用X-Forwarded-for
头中找到的第一个IP地址。这种方法容易受到欺骗,因为恶意客户端可能会为解析程序接受的“x-forwarded-for”设置初始值。
XForwardedRemoteAddressResolver::MaxTrustedIndex
获取一个索引,该索引与在Spring Cloud网关前运行的受信任基础设施数量相关。例如,如果SpringCloudGateway只能通过haproxy访问,则应使用值1。如果在访问Spring Cloud Gateway之前需要两个受信任的基础架构跃点,那么应该使用2。
给定以下的header值:
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3
下面的` maxTrustedIndex值将生成以下远程地址:
Java 配置方式:
GatewayConfig.java
RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
.maxTrustedIndex(1);
...
.route("direct-route",
r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24")
.uri("https://downstream1")
.route("proxied-route",
r -> r.remoteAddr(resolver, "10.10.1.1", "10.10.1.1/24")
.uri("https://downstream2")
)
过滤器允许以某种方式修改传入的HTTP请求或返回的HTTP响应。过滤器的作用域是某些特定路由。Spring Cloud Gateway包括许多内置的 Filter工厂。
注意:有关如何使用以下任何过滤器的更详细示例,请查看unit tests.。
采用一对名称和值作为参数
application.yml.
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
对于所有匹配的请求,这将向下游请求的头中添加 x-request-foo:bar
header
采用一对名称和值作为参数
application.yml.
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: http://example.org
filters:
- AddRequestParameter=foo, bar
对于所有匹配的请求,这将向下游请求添加foo=bar
查询字符串
采用一对名称和值作为参数
application.yml.
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddResponseHeader=X-Response-Foo, Bar
对于所有匹配的请求,这会将x-response-foo:bar
头添加到下游响应的header中
Hystrix 是Netflix开源的断路器组件。Hystrix GatewayFilter允许你向网关路由引入断路器,保护你的服务不受级联故障的影响,并允许你在下游故障时提供fallback响应。
要在项目中启用Hystrix网关过滤器,需要添加对 spring-cloud-starter-netflix-hystrix
的依赖 Spring Cloud Netflix.
Hystrix GatewayFilter Factory 需要一个name参数,即HystrixCommand
的名称。
application.yml.
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: http://example.org
filters:
- Hystrix=myCommandName
这将剩余的过滤器包装在命令名为“myCommandName”的HystrixCommand
中。
hystrix过滤器还可以接受可选的fallbackUri
参数。目前,仅支持forward:
预设的URI,如果调用fallback,则请求将转发到与URI匹配的控制器。
application.yml.
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
当调用hystrix fallback时,这将转发到/incaseoffailureusethis
uri。注意,这个示例还演示了(可选)通过目标URI上的'lb`前缀,使用Spring Cloud Netflix Ribbon 客户端负载均衡。
主要场景是使用fallbackUri
到网关应用程序中的内部控制器或处理程序。但是,也可以将请求重新路由到外部应用程序中的控制器或处理程序,如:
application.yml.
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: Hystrix
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
在本例中,gateway应用程序中没有 fallback
实现,但是另一个应用程序中有一个接口实现,注册为“http://localhost:9994”。
在将请求转发到fallback的情况下,Hystrix Gateway过滤还支持直接抛出Throwable
。它被作为ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR
属性添加到ServerWebExchange
中,可以在处理网关应用程序中的fallback时使用。
对于外部控制器/处理程序方案,可以添加带有异常详细信息的header。可以在 FallbackHeaders GatewayFilter Factory section.中找到有关它的更多信息。
hystrix配置参数(如 timeouts)可以使用全局默认值配置,也可以使用Hystrix wiki中所述属性进行配置。
要为上面的示例路由设置5秒超时,将使用以下配置:
application.yml.
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000
FallbackHeaders
允许在转发到外部应用程序中的FallbackUri
的请求的header中添加Hystrix异常详细信息,如下所示:
application.yml.
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: Hystrix
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
filters:
- name: FallbackHeaders
args:
executionExceptionTypeHeaderName: Test-Header
在本例中,在运行HystrixCommand
发生执行异常后,请求将被转发到 localhost:9994
应用程序中的 fallback
终端或程序。异常类型、消息(如果可用)cause exception类型和消息的头,将由FallbackHeaders
filter添加到该请求中。
通过设置下面列出的参数值及其默认值,可以在配置中覆盖headers的名称:
executionExceptionTypeHeaderName
("Execution-Exception-Type"
)executionExceptionMessageHeaderName
("Execution-Exception-Message"
)rootCauseExceptionTypeHeaderName
("Root-Cause-Exception-Type"
)rootCauseExceptionMessageHeaderName
("Root-Cause-Exception-Message"
)Hystrix 如何实现的更多细节可以参考 Hystrix GatewayFilter Factory section.
PrefixPath GatewayFilter 只有一个 prefix
参数.
application.yml.
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://example.org
filters:
- PrefixPath=/mypath
这将给所有匹配请求的路径加前缀/mypath
。因此,向/hello
发送的请求将发送到/mypath/hello
。
该filter没有参数。设置了该Filter后,GatewayFilter将不使用由HTTP客户端确定的host header ,而是发送原始host header 。
application.yml.
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: http://example.org
filters:
- PreserveHostHeader
RequestRateLimiter使用RateLimiter
实现是否允许继续执行当前请求。如果不允许继续执行,则返回HTTP 429 - Too Many Requests
(默认情况下)。
这个过滤器可以配置一个可选的keyResolver
参数和rate limiter参数(见下文)。
keyResolver
是 KeyResolver
接口的实现类.在配置中,按名称使用SpEL引用bean。#{@myKeyResolver}
是引用名为'myKeyResolver'的bean的SpEL表达式。
KeyResolver.java.
public interface KeyResolver {
Mono resolve(ServerWebExchange exchange);
}
KeyResolver
接口允许使用可插拔策略来派生限制请求的key。在未来的里程碑版本中,将有一些KeyResolver
实现。
KeyResolver
的默认实现是PrincipalNameKeyResolver
,它从ServerWebExchange
检索Principal
并调用Principal.getName()
。
默认情况下,如果KeyResolver
没有获取到key,请求将被拒绝。此行为可以使用 spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key
(true or false) 和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code
属性进行调整。
说明
无法通过"shortcut" 配置RequestRateLimiter。以下示例无效
application.properties.
# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}
Redis的实现基于 Stripe实现。它需要使用 spring-boot-starter-data-redis-reactive
Spring Boot starter。
使用的算法是Token Bucket Algorithm.。
redis-rate-limiter.replenishRate
是你允许用户每秒执行多少请求,而丢弃任何请求。这是令牌桶的填充速率。
``redis-rate-limiter.burstCapacity`是允许用户在一秒钟内执行的最大请求数。这是令牌桶可以保存的令牌数。将此值设置为零将阻止所有请求。
稳定速率是通过在replenishRate
和 burstCapacity
中设置相同的值来实现的。可通过设置burstCapacity
高于replenishRate
来允许临时突发流浪。在这种情况下,限流器需要在两次突发之间留出一段时间(根据replenishRate
),因为连续两次突发将导致请求丢失 (HTTP 429 - Too Many Requests
).。
application.yml.
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.
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
这定义了每个用户10个请求的限制。允许20个突发,但下一秒只有10个请求可用。KeyResolver
是一个简单的获取user
请求参数的工具(注意:不建议用于生产)。
限流器也可以定义为RateLimiter
接口的实现 bean。在配置中,按名称使用SpEL引用bean。#{@myRateLimiter}
是引用名为'myRateLimiter'的bean的SpEL表达式。
application.yml.
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: http://example.org
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@myRateLimiter}"
key-resolver: "#{@userKeyResolver}"
该过滤器有一个 status
和一个 url
参数。status是300类重定向HTTP代码,如301。该URL应为有效的URL,这将是 Location
header的值。
application.yml.
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://example.org
filters:
- RedirectTo=302, http://acme.org
这将发送一个302状态码和一个Location:http://acme.org
header来执行重定向。
RemoveNonProxyHeaders GatewayFilter Factory 从转发请求中删除headers。删除的默认头列表来自 IETF.
The default removed headers are:
spring.cloud.gateway.filter.remove-non-proxy-headers.headers
属性设置为要删除的header名称。有一个name
参数. 这是要删除的header的名称。
application.yml.
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: http://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
这将在X-Request-Foo
header被发送到下游之前删除它。
有一个name
参数. 这是要删除的header的名称。
application.yml.
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: http://example.org
filters:
- RemoveResponseHeader=X-Response-Foo
这将在返回到网关client之前从响应中删除x-response-foo
头。
包含一个 regexp
正则表达式参数和一个 replacement
参数. 通过使用Java正则表达式灵活地重写请求路径。
application.yml.
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: http://example.org
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/(?.*), /$\{segment}
对于请求路径/foo/bar
,将在发出下游请求之前将路径设置为/bar
。注意,由于YAML规范,请使用 $\
替换 $
。
包含 name
, regexp
和 replacement
参数.。通过使用Java正则表达式灵活地重写响应头的值。
application.yml.
spring:
cloud:
gateway:
routes:
- id: rewriteresponseheader_route
uri: http://example.org
filters:
- RewriteResponseHeader=X-Response-Foo, , password=[^&]+, password=***
对于一个/42?user=ford&password=omg!what&flag=true
的header值,在做下游请求时将被设置为/42?user=ford&password=***&flag=true
,由于YAML规范,请使用 $\
替换 $
。
SaveSession GatewayFilter Factory将调用转发到下游之前强制执行WebSession::save
操作。这在使用 Spring Session 之类时特别有用,需要确保会话状态在进行转发调用之前已保存。
application.yml.
spring:
cloud:
gateway:
routes:
- id: save_session
uri: http://example.org
predicates:
- Path=/foo/**
filters:
- SaveSession
如果你希望要将[Spring Security](https://projects.spring.io/Spring Security/)与Spring Session集成,并确保安全详细信息已转发到远程的进程,这一点至关重要。
SecureHeaders GatewayFilter Factory 将许多headers添加到reccomedation处的响应中,从this blog post.
添加以下标题(使用默认值分配):
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
命名空间中设置相应的属性:
Property to change:
xss-protection-header
strict-transport-security
frame-options
content-type-options
referrer-policy
content-security-policy
download-options
permitted-cross-domain-policies
SetPath GatewayFilter Factory 采用 template
路径参数。它提供了一种通过允许路径的模板化segments来操作请求路径的简单方法。使用Spring Framework中的URI模板,允许多个匹配segments。
application.yml.
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: http://example.org
predicates:
- Path=/foo/{segment}
filters:
- SetPath=/{segment}
对于一个 /foo/bar
请求,在做下游请求前,路径将被设置为/bar
SetResponseHeader GatewayFilter Factory 包括 name
和 value
参数.
application.yml.
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: http://example.org
filters:
- SetResponseHeader=X-Response-Foo, Bar
此GatewayFilter使用给定的名称替换所有header,而不是添加。因此,如果下游服务器响应为X-Response-Foo:1234
,则会将其替换为X-Response-Foo:Bar
,这是网关客户端将接收的内容。
SetStatus GatewayFilter Factory 包括唯一的 status
参数.必须是一个可用的Spring HttpStatus
。它可以是整数值404
或字符串枚举NOT_FOUND
。
application.yml.
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返回码将设置为401.
StripPrefix GatewayFilter Factory 包括一个parts
参数。 parts
参数指示在将请求发送到下游之前,要从请求中去除的路径中的节数。
application.yml.
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: http://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2
当通过网关发出/name/bar/foo
请求时,向nameservice
发出的请求将是http://nameservice/foo
。
Retry GatewayFilter Factory包括 retries
, statuses
, methods
和 series
参数.
retries
: 应尝试的重试次数statuses
: 应该重试的HTTP状态代码,用org.springframework.http.HttpStatus
标识methods
: 应该重试的HTTP方法,用 org.springframework.http.HttpMethod
标识series
: 要重试的一系列状态码,用 org.springframework.http.HttpStatus.Series
标识application.yml.
spring:
cloud:
gateway:
routes:
- id: retry_test
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
注意
retry filter 不支持body请求的重试,如通过body的POST 或 PUT请求
注意
在使用带有前缀为forward:
的retry filter
时,应仔细编写目标端点,以便在出现错误时不会执行任何可能导致将响应发送到客户端并提交的操作。例如,如果目标端点是带注解的controller,则目标controller方法不应返回带有错误状态代码的ResponseEntity
。相反,它应该抛出一个Exception
,或者发出一个错误信号,例如通过Mono.error(ex)
返回值,重试过滤器可以配置为通过重试来处理。
当请求大小大于允许的限制时,RequestSize GatewayFilter Factory可以限制请求不到达下游服务。过滤器以RequestSize
作为参数,这是定义请求的允许大小限制(以字节为单位)。
application.yml.
spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
maxSize: 5000000
当请求因大小而被拒绝时, RequestSize GatewayFilter Factory 将响应状态设置为413 Payload Too Large
,并带有额外的header errorMessage
。下面是一个 errorMessage
的例子。
errorMessage
: Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB
注意
如果未在路由定义中作为filter参数提供,则默认请求大小将设置为5 MB。
这个过滤器被定义为beta版本,将来API可能会改变。
此过滤器可用于在请求主体被网关发送到下游之前对其进行修改。
注意
只能使用Java DSL配置此过滤器
@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;
}
}
这个过滤器被定义为beta版本,将来API可能会改变。
此过滤器可用于在将响应正文发送回客户端之前对其进行修改。
注意
只能使用Java DSL配置此过滤器
@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();
}
GlobalFilter
接口与GatewayFilter
具有相同的签名。是有条件地应用于所有路由的特殊过滤器。(此接口和用法可能在将来的里程碑版本中发生更改)。
当请求进入(并与路由匹配)时,筛选Web Handler 会将GlobalFilter
的所有实例和所有的GatewayFilter
路由特定实例添加到 filter chain。filter组合的排序由org.springframework.core.Ordered
接口决定,可以通过实现getOrde()
方法或使用@Order
注释来设置。
由于Spring Cloud Gateway将用于执行过滤器逻辑区分为“前置”和“后置”阶段,具有最高优先级的过滤器将是“前置”阶段的第一个,而“后置”阶段的最后一个。
ExampleConfiguration.java.
@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");
}));
};
}
ForwardRoutingFilter
在exchange属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
中查找URI。如果URL有一个forward
scheme (如 forward:///localendpoint
),它将使用Spring DispatcherHandler
来处理请求。请求URL的路径部分将被转发URL中的路径覆盖。未修改的原始URL将附加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR
属性中的列表中。
LoadBalancerClientFilter
在exchange属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
中查找URI。如果URL有一个lb
scheme (如 lb://myservice
),它将使用Spring Cloud LoadBalancerClient
将名称(在前一个示例中为'myservice)解析为实际主机和端口,并替换URI。未修改的原始URL将附加到
ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性中的列表中。过滤器还将查看
ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR属性,查看它是否等于
lb`,然后应用相同的规则。
application.yml.
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**
注意
默认情况下,如果一个服务实例在LoadBalancer
中没有发现,则返回503。可以通过设置spring.cloud.gateway.loadbalancer.use404=true
来让网管返回404.
注意
从LoadBalancer
返回的ServiceInstance
的isSecure
值将覆盖在对网关发出的请求中指定的scheme。例如,如果请求通过HTTPS
进入网关,但ServiceInstance
表示它不安全,则下游请求将通过HTTP
协议。相反的情况也适用。但是,如果在网关配置中为路由指定了GATEWAY_SCHEME_PREFIX_ATTR
,则前缀将被删除,并且路由URL生成的scheme将覆盖ServiceInstance
配置。
如果位于 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
属性中的URL具有http
或https
模式,则会运行Netty Routing Filter。它使用Netty HttpClient
发出下游代理请求。响应放在 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR
exchange属性中,以便在以后的过滤器中使用。(有一个实验阶段不需要Netty的相同的功能的Filter,WebClientHttpRoutingFilter
)
如果ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR
exchange属性中存在 Netty HttpClientResponse
,则运行 NettyWriteResponseFilter
。它在其他所有过滤器完成后将代理响应写回网关客户端响应之后运行。(有一个不需要netty的实验性的WebClientWriteResponseFilter
执行相同的功能)
如果ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR
exchange属性中存在Route
对象,RouteToRequestUrlFilter
将运行。它基于请求URI创建一个新的URI,使用Route
对象的uri属性进行更新。新的URI被放置在ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
exchange属性中。
如果该URI有一个前缀scheme,例如lb:ws://serviceid
,则会从该URI中剥离该 lb
scheme,并将其放置在ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR
中,以便稍后在过滤器链中使用。
如果ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
exchange属性中有 ws
、 wss
scheme,则Websocket Routing Filter将被运行。它使用Spring Web Socket基础模块将Websocket转发到下游。
URI前缀为lb
的Websockets可以被负载均衡,如 lb:ws://serviceid
.
注意
如果使用 SockJS 作为普通HTTP的fallback,则应配置普通HTTP路由以及WebSocket路由。
application.yml.
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/**
要启用网关指标,请将spring-boot-starter-actuator添加为项目依赖项。然后,默认情况下,只要属性spring.cloud.gateway.metrics.enabled
未设置为false
,网关指标过滤器就会运行。此过滤器添加名为“gateway.requests”的计时器指标,并带有以下标记:
routeId
: The route idrouteUri
: API 将被转发的URIoutcome
: 结果分类依据 HttpStatus.Series status
: 返回client的请求的Http Status这些指标可以从/actuator/metrics/gateway.requests
中获取,可以很容易地与Prometheus集成以创建Grafana dashboard.
注意
要将pometheus启用,需要添加 micrometer-registry-prometheus为项目依赖。
网关路由ServerWebExchange
之后,它将通过向Exchange属性添加gatewayAlreadyRouted
,将该exchange标记为“routed”。一旦一个请求被标记为routed,其他路由过滤器将不会再次路由该请求,将跳过该过滤器。有一些方便的方法可以用来将exchange标记为routed,或者检查exchange是否已经routed。
ServerWebExchangeUtils.isAlreadyRouted
有一个 ServerWebExchange
对象并检查它是否已"routed"ServerWebExchangeUtils.setAlreadyRouted
有一个 ServerWebExchange
对象并将其标记为"routed"网关可以通过常规的 Spring server configuration 来侦听HTTPS上的请求。例子:
application.yml.
server:
ssl:
enabled: true
key-alias: scg
key-store-password: scg1234
key-store: classpath:scg-keystore.p12
key-store-type: PKCS12
网关路由可以路由到HTTP和HTTPS后端。如果路由到HTTPS后端,则可以将网关配置为信任所有具有证书的下游服务:
application.yml.
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true
不建议在生产环境使用不安全的信任管理器。对于生产部署,可以使用一组已知证书配置网关,这些证书可以通过以下方式进行配置:
application.yml.
spring:
cloud:
gateway:
httpclient:
ssl:
trustedX509Certificates:
- cert1.pem
- cert2.pem
如果Spring Cloud Gateway未配置受信任证书,则使用默认信任库(可以使用系统属性javax.net.ssl.trustStore覆盖)。
网关维护一个用于路由到后端的client池。当通过HTTPS通信时,客户端启动一个TLS握手,其中可能会有很多超时。这些超时可以这样配置(显示默认值):
application.yml.
spring:
cloud:
gateway:
httpclient:
ssl:
handshake-timeout-millis: 10000
close-notify-flush-timeout-millis: 3000
close-notify-read-timeout-millis: 0
Spring Cloud Gateway的配置由RouteDefinitionLocator
的集合驱动。
RouteDefinitionLocator.java.
public interface RouteDefinitionLocator {
Flux getRouteDefinitions();
}
默认情况下,PropertiesRouteDefinitionLocator
使用Spring Boot的@ConfigurationProperties
机制加载属性。
以下两个示例是等效的:
application.yml.
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
对于网关的大部分用法,配置文件方式是够用的,但一些生产用例更建议从外部源(如数据库)加载配置。未来的里程碑版本将有基于Spring Data Repositories (如Redis、MongoDB和Cassandra)的RouteDefinitionLocator
实现。
为了可以更简单在Java中配置,在RouteLocatorBuilder
bean中定义了一个fluent API。
GatewaySampleApplication.java.
// static imports from GatewayFilters and RoutePredicates
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
return builder.routes()
.route(r -> r.host("**.abc.org").and().path("/image/png")
.filters(f ->
f.addResponseHeader("X-TestHeader", "foobar"))
.uri("http://httpbin.org:80")
)
.route(r -> r.path("/image/webp")
.filters(f ->
f.addResponseHeader("X-AnotherHeader", "baz"))
.uri("http://httpbin.org:80")
)
.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();
}
这种样式还允许使用更多的自定义断言。由RouteDefinitionLocator
beans定义的断言使用逻辑 and
组合。通过使用fluent Java API,可以在 Predicate
类上使用 and()
、or()
、 negate()
运算符。
可以将网关配置为基于使用兼容DiscoveryClient
注册中心注册的服务来创建路由。
要启用此功能,请设置spring.cloud.gateway.discovery.locator.enabled=true
,并确保DiscoveryClient
实现位于classpath上并已启用(如netflix eureka、consul或zookeeper)。
默认情况下,网关为通过DiscoveryClient
创建的路由定义单个断言和过滤器。
默认断言是使用/serviceId/**
定义的path断言,其中serviceId
是DiscoveryClient
中服务的ID。
默认过滤器是使用正则表达式 /serviceId/(?
和替换的/${remaining}
进行重写。这只是在请求被发送到下游之前从路径中截取掉 service id 。
可以通过设置spring.cloud.gateway.discovery.locator.predicates[x]
and spring.cloud.gateway.discovery.locator.filters[y]
来实现自定义DiscoveryClient
路由使用的断言and/or过滤器。当你这样做时,如果你想要保留这个功能,你需要确保包括上面的默认断言和过滤器。下面是这样一个例子。
application.properties.
spring.cloud.gateway.discovery.locator.predicates[0].name: Path
spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]: "'/'+serviceId+'/**'"
spring.cloud.gateway.discovery.locator.predicates[1].name: Host
spring.cloud.gateway.discovery.locator.predicates[1].args[pattern]: "'**.foo.com'"
spring.cloud.gateway.discovery.locator.filters[0].name: Hystrix
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 + '/(?.*)'"
spring.cloud.gateway.discovery.locator.filters[1].args[replacement]: "'/${remaining}'"
设置 -Dreactor.netty.http.server.accessLogEnabled=true
来开启Reactor Netty access logs,注意必须是Java System Property而不是Spring Boot property。
logging 模块也可以通过配置单独输出一个access log文件,下面是logback的配置例子:
logback.xml.
access_log.log
%msg%n
我们可以通过配置网关来控制CORS行为,全局CORS配置是 Spring Framework CorsConfiguration
模式的URL MAP。
application.yml.
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "http://docs.spring.io"
allowedMethods:
- GET
例子中将允许从docs.spring.io发出的所有GET请求进行CORS请求。
/gateway
的actuator端点允许监视Spring Cloud Gateway应用程序并与之交互。要进行远程访问,必须在应用程序属性中暴露HTTP或JMX 端口。
application.properties.
management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway
要检索应用于所有路由的 [global filters],请get请求 /actuator/gateway/globalfilters
。返回的结果类似于以下内容:
{
"org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@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
}
返回结果包含已就绪的global filters的详细信息(如 org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@77856cc5
)。对于每个global filters,返回结果字符串对应过滤器链中的相应顺序。
要检索应用于路由的 [GatewayFilter factories] ,请get请求/actuator/gateway/routefilters
。返回结果类似于以下内容:
{
"[AddRequestHeaderGatewayFilterFactory@570ed9c configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]": null,
"[SaveSessionGatewayFilterFactory@4449b273 configClass = Object]": null
}
返回结果包含应用于所有路由的GatewayFilter的详细信息。显示每个工厂提供字符串格式的相应对象(例如, [SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]
)。请注意,null
值是由于endpoint controller实现不完整造成的,因为它尝试在filter chain中设置对象的顺序,这不适用于GatewayFilter工厂对象。
如果要清理路由的缓存,请POST请求/actuator/gateway/refresh
。该请求将返回一个没有body的200返回码。
要检索网关中定义的路由,发送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
}]
返回结果中包含网关中所有定义的路由信息,下面表格中描述了返回结果信息:
要获取单个路由的信息,发送GET请求 /actuator/gateway/routes/{id}
(如: /actuator/gateway/routes/first_route
),返回结果如下所示:
{
"id": "first_route",
"predicates": [{
"name": "Path",
"args": {"_genkey_0":"/first"}
}],
"filters": [],
"uri": "http://www.uri-destination.org",
"order": 0
}]
下面表格中描述了返回结果信息:
要创建一个路由,发送POST请求 /gateway/routes/{id_route_to_create}
,参数为JSON结构,具体参数数据结构参考上面章节。
要删除一个路由,发送 DELETE
请求 /gateway/routes/{id_route_to_delete}
。
下表总结了Spring Cloud Gateway actuator endpoints。注意,每个endpoint都是/actuator/gateway
作为基本路径。
TODO: overview of writing custom integrations
TODO: document writing Custom Route Predicate Factories
如果要自定义一个GatewayFilter,需要实现GatewayFilterFactory
。下面是一个你需要集成的抽象类 AbstractGatewayFilterFactory
。
PreGatewayFilterFactory.java.
public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory {
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(request).build());
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
PostGatewayFilterFactory.java.
public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory {
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
}
}
TODO: document writing Custom Global Filters
TODO: document writing Custom Route Locators and Writers
欢迎关注 高广超的简书博客 与 收藏文章 !
欢迎关注 头条号:互联网技术栈 !
个人介绍:
高广超:多年一线互联网研发与架构设计经验,擅长设计与落地高可用、高性能、可扩展的互联网架构。目前从事大数据相关研发与架构工作。
本文首发在 高广超的简书博客 转载请注明!