网关gateway

2020/09/10 Gateway

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

Search

    微信好友

    博士的沙漏

    Table of Contents