# spring cloud gateway ## 项目引入spring cloud gateway 如果要在项目中引入spring cloud gateway,可以在项目pom文件中添加如下依赖: ```xml org.springframework.cloud spring-cloud-starter-gateway ``` > 如果已经在项目中引入了spring-cloud-starter-gateway的依赖,但是不想启用gateway,可以在配置文件中指定`spring.cloud.gateway.enabled=false` > Spring Cloud gateway在运行时需要netty作为运行时环境,并且采用Spring Webflux构建,gateway在传统的基于servlet的环境中无法运行。 ## Spring Cloud gateway核心概念 - Route:gateway的基本构建单元,Route由一个id、一个目标uri,predicate集合、filter集合组成。如果聚合predicate为true,那么route被匹配到 - Predicate:Predicate的输入是` Spring Framework ServerWebExchange`,通过该输入,可以匹配http请求中的任何内容,如header或请求参数等 - Filter:其是GatewayFilter的实例,通过特定的factory构造。在filter中,可以在发送到下游请求之前或者之后对request和response进行修改。 ## Spring Cloud Gateway如何工作 1. 客户端向spring cloud gateway发送请求,如果Gateway Handler Mapping将请求匹配到route,那么会向gateway web handler发送请求 2. gateway web handler通过filter chain对请求进行处理 ## 配置route predicate factories和gateway filter factories 有两种方式来配置predicate和filter:shortcuts和全展开参数 ### shortcut配置方式 ```yml spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: - Cookie=mycookie,mycookievalue ``` ## Route Predicate Factories Spring Cloud Gateway包含了许多内置的route predicate factories,所有的这些predicate都匹配到http请求的不同属性。可以通过逻辑运算符`and`将多个route predicate factory组合起来。 ### After Route Predicate Factory After Route Predicate Factory接收一个参数`datetime`,该参数为ZonedDateTime类型,该Predicate匹配发生在指定时间之后的请求。 ```yml spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] ``` 该route匹配2017-01-20T17:42:47.789-07:00[America/Denver]该时间之后的任意请求 ### Before Route Predicate Factory Before route predicate factory接收一个`datetime`参数,参数为ZonedDateTime类型,该predicate匹配发生在某时间之前的请求。 ```yml spring: cloud: gateway: routes: - id: before_route uri: https://example.org predicates: - Before=2017-01-20T17:42:47.789-07:00[America/Denver] ``` 该route匹配发生在2017-01-20T17:42:47.789-07:00[America/Denver]之前的任意请求 ### Between Route Predicate Factory Between Route Predicate Factory接收两个参数,都是ZonedDateTime类型,该predicate匹配发生在datetime1和datetime2之间的请求,其中第二个参数指定的时间必须位于第一个参数指定时间之后。如下指定了一个between predicate示例: ```yml 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 Route Predicate Factory Cookie Route Predicate Factory接收两个参数,`name`和`regexp`。该predicate匹配请求中含有指定名称,并且cookie值满足`regexp`正则表达式。如下展示了一个cookie predicate示例: ```xml spring: cloud: gateway: routes: - id: cookie_route uri: https://example.org predicates: - Cookie=chocolate, ch.p ``` 上述cookie_route会匹配请求中含有name为chocolate的cookie,并且cookie值满足`ch.p`正则表达式的请求。 ### Header Route Predicate Factory Header Route Predicate Factory接收两个参数,`header`和`regexp`(regexp是正则表达式)。该predicate会匹配一个拥有指定名称header的请求,且header对应的值要满足regexp表达式。 如下示例显示了header route predicate的用法: ```yml spring: cloud: gateway: routes: - id: header_route uri: https://example.org predicates: - Header=X-Request-Id, \d+ ``` ### Host Route Predicate Factory Host Route Predicate Factory接收一个参数:一个pattern列表。pattern是ant-style并用`.`作为分隔符。该predicate会匹配`Host`header,示例如下所示: ```yml spring: cloud: gateway: routes: - id: host_route uri: https://example.org predicates: - Host=**.somehost.org,**.anotherhost.org ``` URI template 变量例如`{sub}.myhost.org`也支持。 该route会匹配如下请求:headers中包含name为Host的header,并且Host header的值为`www.somehost.org`,`beta.somehost.org`,`www.anotherhost.org`等形式 ### Method Route Predicate Factory Method Route Predicate Factory接收一个`methods`参数,其指定了匹配的HTTP METHOD. 如下示例指定了一个method route predicate示例: ```yml spring: cloud: gateway: routes: - id: method_route uri: https://example.org predicates: - Method=GET,POST ``` 该predicate匹配method为GET或POST的任意请求。 ### Path Route Predicate Factory Path Route Predicate Factory接收两个参数,一个PathMatcher Pattern的列表,和一个可选的matchTrailingSlash(默认设置为true)。 如下实例配置了一条path route predicate: ```yml spring: cloud: gateway: routes: - id: path_route uri: https://example.org predicates: - Path=/red/{segment},/blue/{segment} ``` 上述配置的route能够匹配/red/1或/red/1/、/red/blue或/blue/green. 如果`matchTrailingSlash`被设置为false,那么/red/1/将不会被匹配到。 该predicate将URI template变量(例如segment)解析为name、value键值对并且放到ServerWebExchange.getAttributes()中。这些值可以被GatewayFilter factories使用。 可以通过如下的工具类方法来对这些变量进行访问,按如下示例所示: ```java Map uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange); String segment = uriVariables.get("segment"); ``` ### Query Route Predicate Factory Query Route Predicate Factory接收两个参数:必填参数param和可选参数regexp,示例如下所示: ```yml spring: cloud: gateway: routes: - id: query_route uri: https://example.org predicates: - Query=green ``` 上述路由配置,匹配请求参数中含有green参数的请求 ```yml spring: cloud: gateway: routes: - id: query_route uri: https://example.org predicates: - Query=red, gree. ``` 该route则是匹配请求参数含有red,并且其value满足`gree.`正则表达式的请求。 ### RemoteAddr Route Predicate Factory RemoteAddr route predicate factory接收一个`sources`列表(列表最小长度为1),列表元素为ipv4或ipv6字符串,例如`192.168.0.1/1`. 如下显示了一个RemoteAddr route predicate示例: ```yml spring: cloud: gateway: routes: - id: remoteaddr_route uri: https://example.org predicates: - RemoteAddr=192.168.1.1/24 ``` 该route匹配如下请求:如果请求的remote addr满足表达式,例如`192.168.1.10`。 #### 修改Remote Addr的解析方式 默认情况下,RemoteAddr predicate factory的解析方式是使用请求的remote address。但是,如果Spring Cloud Gateway如果位于proxy layer之后,那么remote address匹配的不是实际的client ip。 可以通过自定义`RemoteAddressResolver`来自定义remote address的解析方式。Spring Cloud Gateway附带了一个非默认的remote address reslover,该reslover基于` X-Forwarded-For`header进行解析,该resolver是`XForwardedRemoteAddressResolver`。 XForwardedRemoteAddressResolver有两个静态的构造器方法,分别使用了两种不同的安全方式: - `XForwardedRemoteAddressResolver::trustAll`:返回一个RemoteAddressResolver,会一直从`X-Forwarded-For`中取第一个ip address。该方法很容易收到欺骗,因为恶意客户端程序会设置`X-Forwarded-For`字段的初始值,而此时reslover会接收虚假设置的值。 - `XForwardedRemoteAddressResolver::maxTrustedIndex`:接收一个index,该index和Spring Cloud Gateway之前运行的受信任设施的数量相关。 如存在如下header: ```yml X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3 ``` 如下maxTrustedIndex会产生的remote address将如下所示: | maxTrustedIndex | result | | :-: | :-: | | [Integer.MIN_VALUE,0] | 该范围输入不合法 | | 1 | 0.0.0.3 | | 2 | 0.0.0.2 | | 3 | 0.0.0.1 | | [4, Integer.MAX_VALUE] | 0.0.0.1 | 如下展示了如何通过java代码来完成相同的配置: ```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") ) ``` ### Weight Route Predicate Factory Weight Route Predicate Factory接收两个参数:`group`和`weight`,weight将对每个group进行计算。如下示例配置了一个Weight Route Predicate: ```yml 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 ``` 该route将会把80%的负载转发到https://weighthigh.org,把20%的负载发送到https://weightlow.org ### XForwarded Remote Addr Route Predicate XForwarded Remote Addr Route Predicate接收一个`sources`列表,列表元素为ipv4或ipv6地址,例如`192.168.0.1/16`。 该route predicate允许通过`X-Forwarded-For` header来过滤请求。 该predicate可以和反向代理服务器(通常用作负载均衡或web应用防火墙)一起使用,只有当请求来源于这些受信ip地址列表时,请求才会被允许访问。 ```yml spring: cloud: gateway: routes: - id: xforwarded_remoteaddr_route uri: https://example.org predicates: - XForwardedRemoteAddr=192.168.1.1/24 ``` 上述route,只有当`X-Forwarded-For` header中包含例如`192.168.1.10`的ip时,请求才会被匹配。 ## GatewayFilter Factory Route filters 允许对进入的请求和外出的响应进行修改,Route Filter作用域为特定的route。Spring Cloud Gateway有许多内置的route filter。 ### AddRequestHeader GatewayFilter Factory AddRequestHeader GatewayFilter Factory接收一个name和value参数,如下示例配置了一个AddRequestHeader GatewayFilter: ```yml spring: cloud: gateway: routes: - id: add_request_header_route uri: https://example.org filters: - AddRequestHeader=X-Request-red, blue ``` 上述配置对所有匹配的请求在下游请求头中添加了`X-Request-red:blue`header。 AddRequestHeader可以使用用于匹配path或host的URI变量。URI变量可以在value中使用,并且在运行时自动拓展,如下示例配置了一个使用URI变量的`AddRequestHeader GatewayFilter`: ```yml 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 GatewayFilter Factory AddRequestHeadersIfNotPresent GatewayFilter接收一个name和value对集合,name和value之间通过`:`进行分隔,如下示例配置了一个filter: ```yml 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 ``` 上述配置会添加两个headers`X-Request-Color-1:blue`和`X-Request-Color-2:green`到下游请求的headers中。该操作和`AddRequestHeader`类似,但是AddRequestHeadersIfNotPresent只会当header不存在时才进行添加。如果该header存在,那么会保留该header的原有值。 > 如果要设置一个multi-valued header,可以使用该headerName多次,例如`AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-1:green` `AddRequestHeadersIfNotPresent`也支持URI变量,如下配置了一个使用URI变量的Gateway Filter: ```yml 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 GatewayFilter Factory AddRequestParameter GatewayFilter Factory接收一个name和value参数,如下示例配置了一个filter: ```xml spring: cloud: gateway: routes: - id: add_request_parameter_route uri: https://example.org filters: - AddRequestParameter=red, blue ``` 上述配置会向下游请求中加入请求参数。 AddRequestParameter GatewayFilter可以使用URI变量,使用URI变量的配置如下所示: ```yml spring: cloud: gateway: routes: - id: add_request_parameter_route uri: https://example.org predicates: - Host: {segment}.myhost.org filters: - AddRequestParameter=foo, bar-{segment} ``` ### AddResponseHeader GatewayFilter Factory `AddResponseHeader GatewayFilter Factory接收一个name和value参数,如下配置了一个filter: ```yml spring: cloud: gateway: routes: - id: add_response_header_route uri: https://example.org filters: - AddResponseHeader=X-Response-Red, Blue ``` 该filter添加了`X-Response-Red:Blue` header到下游响应的headers中。 `AddResponseHeader GatewayFilter`可以使用URI变量,如下实例展示了使用URI变量的配置: ```yml spring: cloud: gateway: routes: - id: add_response_header_route uri: https://example.org predicates: - Host: {segment}.myhost.org filters: - AddResponseHeader=foo, bar-{segment} ``` ### CircuitBreaker GatewayFilter Factory CircuitBreaker GatewayFilter Factory使用了Spring Cloud CircuitBreaker API来将网关路由包装到断路器中。 为了启用Spring Cloud CircuitBreaker filter,需要将`spring-cloud-starter-circuitbreaker-reactor-resilience4j`放到classpath路径下,如下示例配置了filter: ```xml spring: cloud: gateway: routes: - id: circuitbreaker_route uri: https://example.org filters: - CircuitBreaker=myCircuitBreaker ``` ### CacheRequestBody GatewayFilter Factory 有一些场景需要读取请求体,因为请求只能被读取一次,因此需要缓存请求体。可以使用CacheRequestBody filter来缓存请求体,在请求被发送到下游之前,并且可以从`exchange`属性中获取请求体。 ```xml spring: cloud: gateway: routes: - id: cache_request_body_route uri: lb://downstream predicates: - Path=/downstream/** filters: - name: CacheRequestBody args: bodyClass: java.lang.String ``` 上述配置获取请求体并且将请求体转为了String类型,后续可以通过`ServerWebExchange.getAttributes()`来获取,key为`ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR`. ### DedupeResponseHeader GatewayFilter Factory DedupeResponseHeader GatewayFilter factory接收一个name参数和一个可选的strategy参数,name是一个用空格分隔的list,list元素为header name。如下配置了一个filter示例: ```yml spring: cloud: gateway: routes: - id: dedupe_response_header_route uri: https://example.org filters: - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin ``` 上述配置中移除了Access-Control-Allow-Credentials和Access-Control-Allow-Origin响应header中的重复值,当gateway cors逻辑和下游逻辑都添加了它们时。 DedupeResponseHeader还有一个可选的strategy参数,默认为RETAIN_FIRST,还可以是RETAIN_LAST和RETAIN_UNIQUE。 ### 默认filters 如果想要添加一个filter并将其应用到所有的routes中,可以使用`spring.cloud.gateway.default-filters`,该属性可以接收一个filter列表。如下定义了一系列默认filter: ```yml spring: cloud: gateway: default-filters: - AddResponseHeader=X-Response-Default-Red, Default-Blue - PrefixPath=/httpbin ``` ## Global Filters GlobalFilter接口的签名和Gateway接口的签名一样。GlobalFilter会在满足条件后应用于所有的routes。 ### 组合Global Filter和Gateway Filter 当请求匹配到一个route之后,filtering web handler将所有的GlobalFilter实例和route对应的Gateway Filter实例添加到一个filter chain中。该filter chain通过`org.springframework.core.Ordered`接口进行排序。filter chain中排序最靠前的filter在pre-phase中位于第一个,并且在post-phase中位于最后一个。 如下配置了一个filter chain: ```java @Bean public GlobalFilter customFilter() { return new CustomGlobalFilter(); } public class CustomGlobalFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("custom global filter"); return chain.filter(exchange); } @Override public int getOrder() { return -1; } } ```