Gateway
Spring Cloud Gateway基于Spring Boot 2,是Spring Cloud的全新项目,该项目提供了一个构建在Spring生态之上的API网关。Spring Cloud Gateway旨在提供一种简单而有效的途径来转发请求,并为它们提供横切关注点,例如:安全性、监控/指标和弹性。
基础应用
Spring Cloud Gateway具有如下特征:
- 基于Java 8编码;
- 支持Spring Framework 5;
- 支持Spring Boot 2;
- 支持动态路由;
- 支持内置到Spring Handler映射中的路由匹配;
- 支持基于HTTP请求的路由匹配(Path、Method、Header、Host等);
- 过滤器作用于匹配的路由;
- 过滤器可以修改下游HTTP请求和HTTP响应(增加/修改头部、增加/修改请求参数、改写请求路径等);
- 通过API或配置驱动;
- 支持Spring Cloud DiscoveryClient配置路由,与服务发现与注册配合使用。
在Finchley正式版之前,Spring Cloud推荐的网关是Netflix提供的Zuul(笔者这里指的都是Zuul 1.x,是一个基于阻塞I/O的API Gateway)。与Zuul相比,Spring Cloud Gateway建立在Spring Framework 5、Project Reactor和Spring Boot 2之上,使用非阻塞API。Spring Cloud Gateway还支持WebSocket,并且与Spring紧密集成,拥有更好的开发体验。Zuul基于Servlet 2.5,使用阻塞架构,它不支持任何长连接,如WebSocket。Zuul的设计模式和Nginx较像,每次I/O操作都是从工作线程中选择一个执行,请求线程被阻塞直到工作线程完成,但是差别是Nginx用C++实现,Zuul用Java实现,而JVM本身会有第一次加载较慢的情况,使得Zuul的性能相对较差。Zuul已经发布了Zuul 2.x,基于Netty、非阻塞、支持长连接,但Spring Cloud目前还没有整合。Zuul 2.x的性能肯定会较Zuul 1.x有较大提升。在性能方面,根据官方提供的基准(benchmark)测试,Spring Cloud Gateway的RPS(每秒请求数)是Zuul的1.6倍。综合来说,Spring Cloud Gateway在提供的功能和实际性能方面,表现都很优异。
网关服务
网关服务提供路由配置、路由断言和过滤器等功能。 需要引入的依赖如下所示:
<!--依赖于WebFlux,必须引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
</dependency>
<!--服务发现组件,排除Web依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--Kotlin依赖-->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<optional>true</optional>
</dependency>
如上引入了Kotlin相关的依赖,这里需要支持Kotlin的路由配置。Spring Cloud Gateway的使用需要排除Web相关的配置,且必须引入WebFlux的依赖,应用服务启动时会检查WebFlux依赖。
路由断言有多种类型,根据请求的时间、Host地址、路径和请求方法等。如下定义的是一个基于路径的路由断言匹配。
@Bean
public RouterFunction<ServerResponse> testFunRouterFunction() {
RouterFunction<ServerResponse> route = RouterFunctions.route(
RequestPredicates.path("/testfun"),
request -> ServerResponse.ok().body(BodyInserters.fromObject("hello")));
return route;
}
当请求的路径为/testfun时,直接返回状态码200,且响应体为hello的字符串。
网关经常需要对路由请求进行过滤,对符合条件的请求进行一些操作,如增加请求头、增加请求参数、增加响应头和断路器等功能。例如下面的示例代码:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
return builder.routes()
.route(r -> r.path("/image/webp")
.filters(f ->
f.addResponseHeader("X-AnotherHeader", "baz"))
.uri("http://httpbin.org")
)
.build();
}
如上代码实现了当请求路径为/image/webp时,网关将请求转发到http://httpbin.org,并对响应进行过滤处理,增加响应的头部X-AnotherHeader:baz。
除了通过Gateway提供的API自定义路由,还可以通过配置进行定义,如下所示:
spring:
cloud:
gateway:
locator:
enabled: true
default-filters:
- AddResponseHeader=X-Response-Default-Foo, Default-Bar
routes:
- id: default_path_to_http
uri: blueskykong.com
order: 10000
predicates:
- Path=/test/**
如上的配置定义了全局过滤器与一条路由。全局过滤器为所有的响应加上头部X-Response-Default-Foo:Default-Bar。另外还定义了id为default_path_to_http的路由,优先级比较低。符合路由断言条件的请求将会转发到blueskykong.com。
Spring Cloud Gateway支持使用Kotlin自定义路由
@Configuration
class AdditionalRoutes {
@Bean
fun additionalRouteLocator(builder: RouteLocatorBuilder): RouteLocator = builder. routes {
route(id = "test-kotlin") {
path("/image/png")
filters {
addResponseHeader("X-TestHeader", "foobar")
}
uri("http://blueskykong.com")
}
}
}
当请求的路径是/image/png,将会转发到http://blueskykong.com,并设置了过滤器,在其响应头中加上了X-TestHeader:foobar头部。
还可以配置WebSocket的网关路由,如下所示:
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Foo, Default-Bar
routes:
- id: websocket_test
uri: ws://localhost:9000
order: 9000
predicates:
- Path=/echo
网关会将外部的WebSocket请求转发到ws://localhost:9000。
网关与服务注册与发现组件进行结合,通过serviceId转发到具体的服务实例。在前面已经引入了相应的依赖,配置文件如下所示。
spring:
cloud:
gateway:
locator:
enabled: true
routes:
- id: service_to_user
uri: lb://user
predicates:
- Path=/user/**
filters:
- StripPrefix=1
上面的配置开启了DiscoveryClient服务发现。路由定义了所有请求路径以/user开头的请求,都将会转发到user服务,并应用路径过滤器截掉路径的第一部分前缀。即访问/user/test的实际请求转换成了http://172.16.1.100:8005/test。
客户端的访问
发送一个请求到网关,由于加上了对应服务的前缀,所以请求地址为http://localhost:9090/user/test。
网关成功负载均衡到user-server,并返回了ok。响应的头部中包含了全局过滤器设置的头部X-Response-Default-Foo:Default-Bar