文章目录
Spring Cloud Gateway提供一个spring生态系统之上的api网关,由Spring5,Spring Boot2和Reactor组成。旨在提供一个简单,并且高效的api路由功能和横切某些关注点,例如安全、监控和弹性化。
一、如何导入使用Spring Cloud Gateway
在引入了Spring Boot和SpringCloud的maven项目中:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
如果引入了SpringCloud Gateway Starter,但不希望启用网关,可在配置文件中设置 spring.cloud.gateway.enabled=false。Spring Cloud Gateway 基于 Spring Boot 2.x、Spring WebFlux 和 Project Reactor 构建。因此,许多熟悉的同步库(例如Spring Data and Spring Security)和你知道的某些模式在使用 Spring Cloud Gateway 时可能不适用。Spring Cloud Gateway 需要 Spring Boot 和 Spring Webflux 提供的 Netty 运行时。它不适用于传统的 Servlet 容器或构建为 WAR 包项目。
二、Gateway相关概念
- Route:gateway中最基本的组成块,由一个ID,一个目的URI,一组断言,和一组过滤器组成。当组中断言都为真时才会匹配该路由。
- Predicate:这是一个java8中的函数式接口Predicate,输入类型是 Spring Framework ServerWebExchange。这使你可以匹配来自 HTTP 请求的任何内容,例如标头或参数。
- Filter:Filter是使用特定工厂构建的 GatewayFilter 实例。在Filter中,你可以在发送下游请求之前或之后修改请求和响应。
routes:
- id: gby-auth
uri: lb://gby-auth
predicates:
- Path=/api/auth/**
filters:
- StripPrefix=2
三、Gateway工作流程
下图提供了SpringCloud Gateway如何工作的高级抽象表示:
客户端发起请求到Spring Cloud Gateway,如果Gateway Handler Mapping判断该请求匹配了一个route,该请求就会被发送给Gateway Web Handler。Gateway Web Handler通过一组过滤器链对请求进行过滤处理。当过滤器执行完后,会生成一个代理请求,代理请求再将响应交给后置过滤器进行过滤处理,然后将过滤后的响应交给Gateway Web Handler,Gateway Web Handler再交给Gateway Handler Mapping,Gateway Handler Mapping返回给请求客户端。
四、配置路由断言和网关过滤器
有两种方式可以配置断言和过滤器,简洁方式和全限定方式
1、简洁方式
简洁方式通过过滤器名称识别,后接等于号(=),等于号后就是名称和匹配规则,例如:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- Cookie=mycookie,mycookievalue
该例中,断言的过滤类型为Cookie,cookie的name为mycookie,值为mycookievalue,也就是说当请求的Cookie中有mycookie这个cookie并且值为mycookievalue时才会匹配该路由。
2、全限定方式
全限定方式就是要将一个断言的各个部分具体指明:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue
五、断言的种类
1、After Predicate
After Predicate接收一个时间戳参数,在该时间戳之后的请求都视为匹配:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
此路由匹配 2017 年 1 月 20 日 17:42 (美国丹佛)之后提出的任何请求。
2、Before Predicate
与After类似,该Predicate匹配指定时间戳之前的任何请求:
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
此路由匹配 2017 年 1 月 20 日 17:42 (美国丹佛)之前提出的任何请求。
3、Between Predicate
Between Predicate接收两个时间戳参数,匹配请求时间为两个时间戳之间的请求:
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]
此路由匹配 2017 年 1 月 20 日 17:42 (美国丹佛)之后和 2017 年 1 月 21 日 17:42(美国丹佛)之前的任何请求。这对于维护窗口可能很有用。
4、Cookie Predicate
Cookie Predicate接收两个参数,cookie名和关于cookie值的正则表达式,匹配具有给定名称且其值与正则表达式匹配的 cookie。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
此路由匹配具有名为 Chocolate 的 cookie 的请求,该 cookie 的值与 ch.p 正则表达式匹配。
5、Header Predicate
和Cookie Predicate类似,Header Predicate接收两个参数,一个name和关于其值的正则表达式,匹配具有给定name的标头且该标头的值需与正则表达式匹配:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
如果请求具有名为 X-Request-Id 的标头,其值与 \d+ 正则表达式匹配(即,它具有一个或多个数字的值),则此路由匹配。
6、Host Predicate
Host Predicate接收一个参数:主机名模式列表。该模式是 Ant 风格的模式,带有 .作为分隔符。匹配与模式列表中的模式匹配的 Host 标头:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
如果请求具有值为 www.somehost.org 或 beta.somehost.org 或 www.anotherhost.org 的 Host 标头,则此路由匹配。
7、Method Predicate
Method Predicate接收一个方法参数,可以是一个或多个(以逗号分割):来匹配指定的 HTTP 方法。
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
如果请求方法是 GET 或 POST,则此路由匹配。
8、Path Predicate
Path Route Predicate 接受两个参数:Spring PathMatcher 模式列表和名为 matchOptionalTrailingSeparator 的可选标志:
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
如果请求路径是/red/1 或 /red/blue 或 /blue/green,则此路由匹配。
该Predicate会提取 URI 中的模板变量(例如前面示例中定义的segment)作为名称和值的映射,并使用在 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE 中定义的键将其放置在 ServerWebExchange.getAttributes() 中。然后这些值可供 GatewayFilte使用。
Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);
String segment = uriVariables.get("segment");
六、Gateway中的内置Filter
Route Filter允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。Spring Cloud Gateway 包括许多内置的 GatewayFilter。
1、PrefixPath Filter
PrefixPath GatewayFilter 接收一个前缀参数,将该前缀加在请求路径之前:
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
将 /mypath 前缀为所有匹配请求的路径。因此,对 /hello 的请求将被发送到 /mypath/hello。
2、StripPrefix Filter
StripPrefix Filter接收一个参数,该参数指明路径剥去的层数,例如:
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: https://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2
当通过网关向 /name/blue/red 发出请求时,对 nameservice 发出的请求看起来像 https://nameservice/red。
七、Global Filter
GlobalFilter接口与GatewayFilter具有相同的功能,你可以通过实现GlobalFilter接口来编写你自己的过滤处理,SpringCloud Gateway也提供了许多内置的GlobalFilter,这些特殊的过滤器在特定条件下会应用于所有的路由。
1、整合GlobalFilter和GatewayFilter的顺序
当一个请求匹配一个路由时,web过滤器handler会将所有的GlobalFilter实例和匹配该路由的所有GatewayFilter实例添加到过滤器链。过滤器链中过滤器的顺序通过org.springframework.core.Ordered接口的getOrder()方法来决定。
由于 Spring Cloud Gateway 区分过滤器逻辑执行的“前”和“后”阶段,具有最高优先级的过滤器是“前”阶段的第一个和“后”阶段的最后一个 阶段。
以下代码示例了如何配置过滤器在过滤器链中的顺序:
@Bean
public GlobalFilter customFilter(){
return new CustomeGlobalFilter();
}
public class CustomeGlobalFilter 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;
}
}
八、TLS和SSL
可以通过简单的配置来使Gateway用于HTTPS请求,例如:
server:
ssl:
enabled: true
key-alias: scg
key-store-password: scg1234
key-store: classpath:scg-keystore.p12
key-store-type: PKCS12
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true
使用一个不安全的trust manager并不适合生产环境,更好是配置一些著名的证书,例如:
spring:
cloud:
gateway:
httpclient:
ssl:
trustedX509Certificates:
- cert1.pem
- cert2.pem
gateway维护了一个client池来将请求路由到后端,当使用HTTPS通信时,client会启动TLS握手,许多超时与此握手相关,你可以像下面这样配置这些超时时间:
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 接口定义如下:
public interface RouteDefinitionLocator{
Flux<RouteDefinition> getRouteDefinitions();
}
默认情况下,通过SpringBoot的@ConfigurationProperties机制将配置文件中的配置读取到PropertiesRouteDefinitionLocator。
在某些gateway的使用场景中,配置文件进行属性配置是足够的,但在某些情况下需要从数据库、redis、MongoDB中加载配置文件,这时可以通过实现RouteDefinitionRepository的 refreshRoutes()方法实现。
十、Route元数据配置
可以使用元数据为每个路由配置附加参数,如下所示:
spring:
cloud:
gateway:
routes:
- id: route_with_metadata
uri: https://example.org
metadata:
optionName: "OptionValue"
compositeObject:
name: "value"
iAmNumber: 1
然后从exchange中获取它们:
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
// 获取所有元数据
route.getMetadata();
// 根据key获取一个元数据
route.getMetadata("optionName");
十一、HTTP超时配置
HTTP超时(包括响应超时和连接超时)可以被应用于所有的Route或部分限定路由。
1、Global timeouts
意如其名,用于配置http全局超时
spring:
cloud:
gateway:
httpclient:
# 单位毫秒
connect-timeout: 1000
response-timeout: 5s
2、单个路由的超时
- id: per_route_timeouts
uri: https://example.org
predicates:
- name: Path
args:
pattern: /delay/{timeout}
metadata:
response-timeout: 200
connect-timeout: 200
或者通过Java代码:
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);
}).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();
}
3、Discovery 路由配置
你可以将网关配置为基于向 DiscoveryClient 兼容的服务注册表注册的服务创建路由。
要启用此功能,需设置 spring.cloud.gateway.discovery.locator.enabled=true 并确保 DiscoveryClient 的实现(例如 Netflix Eureka、Consul 或 Zookeeper)在类路径上并已启用。
十二、跨域配置
你可以通过配置来控制gateway的跨域行为,“全局”CORS 配置是 URL 模式到 Spring Framework CorsConfiguration 的映射。 以下示例配置 CORS:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET
在上面的例子中,允许来自docs.spring.io的所有get跨域请求。