这是基于 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。可以判断请求中任何内容
- java8的函数Predicate,入参为
- 过滤器 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
需要
- 引入项目
- com.github.ben-manes.caffeine:caffeine
- spring-boot-starter-cache
- 开启本地缓存配置
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 \ 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 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();
}