微服务网关之SpringCloudGateway

一、SpringCloudGateway

1.1 简介

SpringCloud GatewaySpring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在 Spring Cloud 2.0 以上版本中,没有对新版本的 Zuul 2.0 以上最新高性能版本进行集成,仍然还是使用的 Zuul 2.0 之前的 非Reactor 模式 的老版本。而为了提升网关的性能,SpringCloud Gateway 是基于 WebFlux 框架实现的,而 WebFlux 框架底层则使用了高性能的 Reactor 模式通信框架 Netty

Spring Cloud Gateway 不仅提供统一的路由方式,并且基于 Filter 链 的方式提供了网关基本的功能,例如:安全,监控/指标,和限流等。

官网文档

1.2 名词解释

  • Filter(过滤器):

    和 Zuul 的过滤器类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为 org.springframework.cloud.gateway.filter.GatewayFilter 类的实例。
    
  • Route(路由):

    网关配置的基本组成模块,和 Zuul 的路由配置模块类似。一个 Route 模块 由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。
    
  • Predicate(断言):

    这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的 输入类型是一个 ServerWebExchange。
    

1.3 Gateway 处理流程

客户端向 Spring Cloud Gateway 发出请求,在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web HandlerHandler 通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

二、准备工作

2.1 创建 OrderService 模块

  1. 添加依赖
<properties>
    <java.version>1.8</java.version>
    <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
    <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
</dependencies>	
  1. 编写配置文件
spring:
  application:
    # 服务名称
    name: order-service

  cloud:
    nacos:
      discovery:
        # 注册服务中心地址
        server-addr: 192.168.205.10:8848
server:
  # 设置服务端口
  port: 8881            
  1. 创建 OrderController
@RestController
@RequestMapping("/order")
public class OrderController {

    @RequestMapping("/create")
    public String create() {
        return "订单创建成功";
    }

}

4) 启动应用,访问 http://localhost:8881/order/create ,返回:

订单创建成功

2.2 创建网关模块

  1. 添加依赖
<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
    <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

三、路由配置方式

3.1 基于配置文件指定URI路由配置方式

spring:
  application:
    # 配置应用名称
    name: gateway
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: http://localhost:8881
          predicates:
            - Path=/order/**
server:
  # 配置应用端口
  port: 8080

配置说明:

* routes : 表示路由配置,可以存在多个
* id:自定义的路由 ID,保持唯一
* uri:目标服务地址
* predicates::路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
*- Path:基于路径路由     

上面的配置用文字表达如下:

配置一个 Id 为 order-service 的路由规则,当访问地址以 /order 开头时,将会把请求转发到 http://localhost:8881 上去

3.2 基于代码的路由配置方式

@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }

    @Bean
    public RouteLocator orderRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path_route", r -> r.path("/order/**")
                        .uri("http://localhost:8881"))
                .build();
    }
}

3.3 与注册中心相结合的路由配置方式

  1. 添加依赖
<dependencyManagement>
    <dependencies>
        ......
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    <dependencies>    
<dependencyManagement>   

<dependencies>    
	<dependency>
		<groupId>com.alibaba.cloud</groupId>
		<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
	</dependency>    
</dependencies>    
  1. 调整配置
spring:
  application:
    # 配置应用名称
    name: gateway
  cloud:
    nacos:
      discovery:
        # 注册服务中心地址
        server-addr: 192.168.205.10:8848
    gateway:
      routes:
        - id: order-service
          # 与单个URL的区别仅仅在于URI的schema协议不同
          uri: lb://order-service
          predicates:
            - Path=/order/**
server:
  # 配置应用端口
  port: 8080

四、匹配规则

Spring Cloud Gateway 是通过 Spring WebFluxHandlerMapping 做为底层支持来匹配到转发路由,它内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。

4.1 Predicate 断言条件介绍

Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。

Spring Cloud GatewaySpring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。下图总结了 Spring Cloud 内置的几种 Predicate 的实现:

4.2 匹配方式

方式名称 参数名 实例
通过请求参数匹配 Query - Query=name,aaa
通过 Header 属性匹配 Header - Header=X-Request-Id, \d+
通过Cookie匹配 Cookie - Cookie=sessionId,1001
通过 Host 匹配 Host - Host=..com
通过请求方式匹配 Method - Method=GET
通过请求路径匹配 Path - Path=/order/**
通过请求 ip 地址进行匹配 RemoteAddr - RemoteAddr=192.168.1.1/24

4.3 通过请求参数匹配实例

Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。

spring:
  application:
    # 配置应用名称
    name: gateway
  cloud:
    nacos:
      discovery:
        # 注册服务中心地址
        server-addr: 192.168.205.10:8848
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Query=name

只有包含属性名 name 才会转发,如 http://localhost:8080/order/create?name=aaa

Query 值可以以键值对的方式进行配置:

spring:
  application:
    # 配置应用名称
    name: gateway
  cloud:
    nacos:
      discovery:
        # 注册服务中心地址
        server-addr: 192.168.205.10:8848
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Query=name,aaa

只有当请求中包含属性名 name 并且值为 aaa 才会转发 ,http://localhost:8080/order/create?name=aaa

4.3 通过 Header 属性匹配实例

spring:
  application:
    # 配置应用名称
    name: gateway
  cloud:
    nacos:
      discovery:
        # 注册服务中心地址
        server-addr: 192.168.205.10:8848
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
			# \d 表示匹配数字,+ 表示1 次或多次匹配。
            - Header=X-Request-Id, \d+

通过 postman 发起请求,在 header 头上增加属性 X-Request-Id ,配置值为任意数值,如 99,即可访问成功。

五、Filter

路由过滤器允许以某种方式修改传入的 HTTP 请求传出的 HTTP 响应,路由过滤器适用于特定路由。

5.1 生命周期

SpringCloudGateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:prepost

  • PRE: 过滤器在请求被路由之前调用。可用来实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • POST:过滤器在路由到微服务以后执行。可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

5.3 分类

Spring Cloud GatewayFilter 从作用范围可分为两种 GatewayFilterGlobalFilter

  • GatewayFilter:应用到单个路由或者一个分组的路由上。
  • GlobalFilter:应用到所有的路由上。

5. 2 GatewayFilter 内置工厂

Spring Cloud Gateway 包括许多内置的 GatewayFilter工厂

工厂名称 作用
AddRequestHeaderGatewayFilterFactory 添加请求头的过滤器工厂
AddRequestParameterGatewayFilterFactory 添加请求参数的过滤器工厂
AddResponseHeaderGatewayFilterFactory 添加响应头的过滤器工厂
DedupeResponseHeaderGatewayFilterFactory 删除重复响应头的过滤器工厂
HystrixGatewayFilterFactory Hystrix 过滤器工厂
PrefixPathGatewayFilterFactory 添加前缀路径的过滤器工厂
PreserveHostHeaderGatewayFilterFactory 保留原请求头的过滤器工厂
RequestRateLimiterGatewayFilterFactory 保留限流头的过滤器工厂
RedirectToGatewayFilterFactory 重定向的过滤器工厂
RemoveRequestHeaderGatewayFilterFactory 删除请求头的过滤器工厂
RemoveResponseHeaderGatewayFilterFactory 删除响应头的过滤器工厂
RemoveRequestParameterGatewayFilterFactory 删除请求参数的过滤器工厂
RewritePathGatewayFilterFactory 重写路径的过滤器工厂
RewriteLocationResponseHeaderGatewayFilterFactory 重写本地响应头的过滤器工厂
RewriteResponseHeaderGatewayFilterFactory 重写响应头的过滤器工厂
SaveSessionGatewayFilterFactory 保存 session 的过滤器工厂
SecureHeadersGatewayFilterFactory 安全头的过滤器工厂
SetPathGatewayFilterFactory 设置路径的过滤器工厂
SetRequestHeaderGatewayFilterFactory 设置请求的过滤器工厂
SetResponseHeaderGatewayFilterFactory 设置响应的过滤器工厂
SetStatusGatewayFilterFactory 设置状态的过滤器工厂
StripPrefixGatewayFilterFactory StripPrefix过滤器工厂
RetryGatewayFilterFactory 重试过滤器工厂
RequestSizeGatewayFilterFactory 请求大小限制过滤器工厂
SetRequestHostGatewayFilterFactory 设置主机的过滤器工厂

5.3 实例之AddRequestHeaderGatewayFilterFactory

spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          filters:
            - AddRequestHeader=age,18

过滤器工厂会在匹配的请求头加上一对请求头,名称为 age,值为18

5.4 自定义过滤器

除了使用默认的内置过滤器,我们还可以自己实现过滤器。

  1. 创建自定义过滤器
public class MyGatewayFilter implements GatewayFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(MyGatewayFilter.class);

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("this is a pre filter");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("this is a post filter");
        }));
    }


    /**
     * 设定优先级别的,值越大则优先级越低
     *
     * @return
     */
    public int getOrder() {
        return 0;
    }
}
  1. 路由配置过滤器
@Bean
public RouteLocator orderRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("path_route", r -> r.path("/order/**")
                    .filters(f -> f.filter(new MyGatewayFilter()))
                    .uri("http://localhost:8881")
                    .order(0)
            )
            .build();
}
  1. 启动项目,访问 http://localhost:8080/order/create,查看控制台输出:
2020-09-07 15:23:17.612  INFO 15348 --- [ctor-http-nio-2] c.l.c.gateway.filter.MyGatewayFilter     : this is a pre filter
2020-09-07 15:23:18.427  INFO 15348 --- [ctor-http-nio-4] c.l.c.gateway.filter.MyGatewayFilter     : this is a post filter

5.5 自定义过滤器工厂

通过自定义过滤器工厂,可以实现在配置文件中配置过滤器。

过滤器工厂的顶级接口是 GatewayFilterFactory ,有2个两个较接近具体实现的抽象类,分别为AbstractGatewayFilterFactoryAbstractNameValueGatewayFilterFactory,前者接收一个参数,比如它的实现类 RedirectToGatewayFilterFactory ;后者接收2个参数,比如它的实现类AddRequestHeaderGatewayFilterFactory 类。

posted @ 2020-09-03 15:28  MarkLogZhu  阅读(762)  评论(0编辑  收藏  举报