前段时间刚刚发布了Spring Boot 2正式版,Spring Cloud Gateway基于Spring Boot 2,是Spring Cloud的全新项目,该项目提供了一个构建在Spring 生态之上的API网关,包括:Spring 5,Spring Boot 2和Project Reactor。 Spring Cloud Gateway旨在提供一种简单而有效的途径来发送API,并为他们提供横切关注点,例如:安全性,监控/指标和弹性。当前最新的版本是v2.0.0.M8
,正式版最近也会到来。
Spring Cloud Gateway的特征:
Zuul基于servlet 2.5(使用3.x),使用阻塞API。 它不支持任何长连接,如websockets。而Gateway建立在Spring Framework 5,Project Reactor和Spring Boot 2之上,使用非阻塞API。 Websockets得到支持,并且由于它与Spring紧密集成,所以将会是一个更好的开发体验。
笔者最近研读了Spring Cloud Gateway的源码,大部分功能的实现也写了源码分析的文章,但毕竟正式版没有发布,本文算是一篇入门实践,展示常用的几个功能,期待最近的正式版本发布。
示例启动两个服务:Gateway-Server和user-Server。模拟的场景是,客户端请求后端服务,网关提供后端服务的统一入口。后端的服务都注册到服务发现Consul(搭建zk,Eureka都可以,笔者比较习惯使用consul)。网关通过负载均衡转发到具体的后端服务。
用户服务注册到Consul上,并提供一个接口/test
。
需要的依赖如下:
1 <dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-starter-consul-discovery</artifactId>
4 </dependency>
5
6 <dependency>
7 <groupId>org.springframework.boot</groupId>
8 <artifactId>spring-boot-starter-web</artifactId>
9 </dependency>
1spring:
2 application:
3 name: user-service
4 cloud:
5 consul:
6 host: 192.168.1.204
7 port: 8500
8 discovery:
9 ip-address: ${HOST_ADDRESS:localhost}
10 port: ${SERVER_PORT:${server.port}}
11 healthCheckPath: /health
12 healthCheckInterval: 15s
13 instance-id: user-${server.port}
14 service-name: user
15server:
16 port: 8005
17management:
18 security:
19 enabled: false
1@SpringBootApplication
2@RestController
3@EnableDiscoveryClient
4public class GatewayUserApplication {
5
6 public static void main(String[] args) {
7 SpringApplication.run(GatewayUserApplication.class, args);
8 }
9
10 @GetMapping("/test")
11 public String test() {
12 return "ok";
13 }
14}
暴露/test
接口,返回ok即可。
网关服务提供多种路由配置、路由断言工厂和过滤器工厂等功能。
需要引入的依赖:
1<dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-actuator</artifactId>
4</dependency>
5//依赖于webflux,必须引入
6<dependency>
7 <groupId>org.springframework.boot</groupId>
8 <artifactId>spring-boot-starter-webflux</artifactId>
9</dependency>
10<dependency>
11 <groupId>org.springframework.cloud</groupId>
12 <artifactId>spring-cloud-gateway-core</artifactId>
13</dependency>
14//服务发现组件,排除web依赖
15<dependency>
16 <groupId>org.springframework.cloud</groupId>
17 <artifactId>spring-cloud-starter-consul-discovery</artifactId>
18 <version>2.0.0.M6</version>
19 <exclusions>
20 <exclusion>
21 <groupId>org.springframework.boot</groupId>
22 <artifactId>spring-boot-starter-web</artifactId>
23 </exclusion>
24 </exclusions>
25</dependency>
26//kotlin依赖
27<dependency>
28 <groupId>org.jetbrains.kotlin</groupId>
29 <artifactId>kotlin-stdlib</artifactId>
30 <version>${kotlin.version}</version>
31 <optional>true</optional>
32</dependency>
33<dependency>
34 <groupId>org.jetbrains.kotlin</groupId>
35 <artifactId>kotlin-reflect</artifactId>
36 <version>${kotlin.version}</version>
37 <optional>true</optional>
38</dependency>
如上引入了kotlin相关的依赖,这里需要支持kotlin的路由配置。Spring Cloud Gateway的使用需要排除web相关的配置,引入的是webflux的引用,应用启动时会检查,必须引入。
路由断言工厂有多种类型,根据请求的时间、host、路径、方法等等。如下定义的是一个基于路径的路由断言匹配。
1 @Bean
2 public RouterFunction<ServerResponse> testFunRouterFunction() {
3 RouterFunction<ServerResponse> route = RouterFunctions.route(
4 RequestPredicates.path("/testfun"),
5 request -> ServerResponse.ok().body(BodyInserters.fromObject("hello")));
6 return route;
7 }
当请求的路径为/testfun
时,直接返回ok的状态码,且响应体为hello
字符串。
网关经常需要对路由请求进行过滤,进行一些操作,如鉴权之后构造头部之类的,过滤的种类很多,如增加请求头、增加请求参数、增加响应头和断路器等等功能。
1 @Bean
2 public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
3 //@formatter:off
4 return builder.routes()
5 .route(r -> r.path("/image/webp")
6 .filters(f ->
7 f.addResponseHeader("X-AnotherHeader", "baz"))
8 .uri("http://httpbin.org:80")
9 )
10 .build();
11 //@formatter:on
12 }
如上实现了当请求路径为/image/webp
时,将请求转发到http://httpbin.org:80
,并对响应进行过滤处理,增加响应的头部X-AnotherHeader: baz
。
上面两小节属于API自定义路由,还可以通过配置进行定义:
1spring:
2 cloud:
3 gateway:
4 locator:
5 enabled: true
6 default-filters:
7 - AddResponseHeader=X-Response-Default-Foo, Default-Bar
8
9 routes:
10 # =====================================
11 - id: default_path_to_http
12 uri: blueskykong.com
13 order: 10000
14 predicates:
15 - Path=/**
如上的配置定义了路由与过滤器。全局过滤器将所有的响应加上头部X-Response-Default-Foo: Default-Bar
。定义了id为default_path_to_http
的路由,只是优先级比较低,当该请求都不能匹配时,将会转发到blueskykong.com
。
Spring Cloud Gateway可以使用kotlin自定义路由:
1@Configuration
2class AdditionalRoutes {
3
4 @Bean
5 fun additionalRouteLocator(builder: RouteLocatorBuilder): RouteLocator = builder.routes {
6 route(id = "test-kotlin") {
7 path("/image/png")
8 filters {
9 addResponseHeader("X-TestHeader", "foobar")
10 }
11 uri("http://httpbin.org:80")
12 }
13 }
14
15}
当请求的路径是/image/png
,将会转发到http://httpbin.org:80
,并设置了过滤器,在其响应头中加上了X-TestHeader: foobar
头部。
与服务注册于发现组件进行结合,通过serviceId转发到具体的服务实例。在前面的配置已经引入了相应的依赖。
1 @Bean
2 public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
3 return new DiscoveryClientRouteDefinitionLocator(discoveryClient);
4 }
将DiscoveryClient
注入到DiscoveryClientRouteDefinitionLocator
的构造函数中,关于该路由定义定位器,后面的源码分析会讲解,此处不展开。
1spring:
2 cloud:
3 gateway:
4 locator:
5 enabled: true
6 default-filters:
7 - AddResponseHeader=X-Response-Default-Foo, Default-Bar
8 routes:
9 # =====================================
10 - id: service_to_user
11 uri: lb://user
12 order: 8000
13 predicates:
14 - Path=/user/**
15 filters:
16 - StripPrefix=1
上面的配置开启了DiscoveryClient
定位器的实现。路由定义了,所有请求路径以/user
开头的请求,都将会转发到user
服务,并应用路径的过滤器,截取掉路径的第一部分前缀。即访问/user/test
的实际请求转换成了lb://user/test
。
还可以配置websocket的网关路由:
1spring:
2 cloud:
3 gateway:
4 default-filters:
5 - AddResponseHeader=X-Response-Default-Foo, Default-Bar
6
7 routes:
8 - id: websocket_test
9 uri: ws://localhost:9000
10 order: 9000
11 predicates:
12 - Path=/echo
启动一个ws服务端wscat --listen 9000
,将网关启动(网关端口为9090),进行客户端连接即可wscat --connect ws://localhost:9090/echo
。
上述实现的功能,读者可以自行下载源码进行尝试。笔者这里只展示访问用户服务的结果:
网关成功负载均衡到user-server,并返回了ok。响应的头部中包含了全局过滤器设置的头部X-Response-Default-Foo: Default-Bar
在本文中,我们探讨了属于Spring Cloud Gateway的一些功能和组件。 这个新的API提供了用于网关和代理支持的开箱即用工具。期待Spring Cloud Gateway 2.0正式版。
https://github.com/keets2012/Spring-Cloud_Samples