一、网关定义

可参考《网关》
  网关(Gateway)又称网间连接器协议转换器。网关在网络层以上实现网络互连,是复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关既可以用于广域网互连,也可以用于局域网互连。 网关是一种充当转换重任的计算机系统或设备。使用在不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。与网桥只是简单地传达信息不同,网关对收到的信息要重新打包,以适应目的系统的需求。同层–应用层。

下面引用《百度百科》的图:

小不点和小明 (很小,这里你就是一个 url 地址,指向某个网页资源)
小不点家长和小明家长(网关)
班主任(DNS 服务器)

二、API 网关

网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
  微服务架下,服务之间容易形成网状的调用关系,这种网状的调用关系不便管理和维护,这种场景下 API 网关应运而生。作为后端服务的入口,API 网关在微服务架构中尤其重要,在对外部系统提供 API 入口的要求下,API 网关应具备路由转发、负载均衡、限流熔断、权限控制、轨迹追踪和实时监控等功能。
  目前,很多微服务都基于的 Spring Cloud 生态构建。Spring Cloud 生态为我们提供了两种 API 网关产品,分别是 Netflix 开源的 Zuul1 和 Spring 自己开发的 Spring Cloud Gateway(下边简称为 Gateway)。Spring Cloud 以 Finchley 版本为分界线,Finchley 版本发布之前使用 Zuul1 作为 API 网关,之后更推荐使用 Gateway。
  虽然 Netflix 已经在 2018 年 5 月开源了 Zuul2,但是 Spring Cloud 已经推出了 Gateway,并且在 github 上表示没有集成 Zuul2 的计划。所以从 Spring Cloud 发展的趋势来看,Gateway 代替 Zuul 是必然的。

三、Gateway 与 Zuul 对比

具体可参考:《Zuul1 与 Spring Cloud Gateway 对比》
1、Zuul1 简介
  Zuul1 是 Netflix 在 2013 年开源的网关组件,大规模的应用在 Netflix 的生产环境中,经受了实践考验。它可以与 Eureka、Ribbon、Hystrix 等组件配合使用,实现路由转发、负载均衡、熔断等功能。Zuul1 的核心是一系列过滤器,过滤器简单易于扩展,已经有一些三方库如 spring-cloud-zuul-ratelimit 等提供了过滤器支持。
  Zuul1 基于 Servlet 构建,使用的是阻塞的 IO,引入了线程池来处理请求。每个请求都需要独立的线程来处理,从线程池中取出一个工作线程执行,下游微服务返回响应之前这个工作线程一直是阻塞的。
  
2、Spring Cloud Gateway 简介
  Spring Cloud Gateway 是 Spring Cloud 的一个全新的 API 网关项目,目的是为了替换掉 Zuul1。Gateway 可以与 Spring Cloud Discovery Client(如 Eureka)、Ribbon、Hystrix 等组件配合使用,实现路由转发、负载均衡、熔断等功能,并且 Gateway 还内置了限流过滤器,实现了限流的功能。
  Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,Spring Cloud Gateway 旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控 / 埋点,和限流等。。使用Netty作为运行时环境,比较完美的支持异步非阻塞编程。Netty 使用非阻塞的 IO,线程处理模型建立在主从 Reactors 多线程模型上。其中 Boss Group 轮询到新连接后与 Client 建立连接,生成 NioSocketChannel,将 channel 绑定到 Worker;Worker Group 轮询并处理 Read、Write 事件。
  目前最流行,用的响应式编程,吞吐能力非常强。响应式编程可参考《响应式编程 函数式编程 简介》

四、微服务架构中网关的位置

五、Spring Cloud Gateway 的特性

(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 进行构建;
(2)动态路由:能够匹配任何请求属性;
(3)可以对路由指定Predicate(断言)Filter(过滤器)
(4)集成 Hystrix 的断路器功能;
(5)集成 Spring Cloud 服务发现功能;
(6)易于编写的 Predicate(断言)和 Filter(过滤器);
(7)请求限流功能;
(8)支持路径重写;

(9)基于异步非阻塞(AIO)模型上进行开发的;

六、为什么选择 Gateway?

neflix 不太靠谱,zuul2.0 一直跳票,迟迟不发布。
  一方面因为 Zuul1.0 已经进入了维护阶段,而且 gateway 是 SpringCloud 团队研发的,是亲儿子产品,值得信赖。Gateway 是基于异步非阻塞模型上进行开发的,性能方面不需要担心。虽然 Netflix 早就发布了 Zuul2.x,但是 SpringCloud 貌似没有整合计划。而且 Netflix 相关组件都宣布进入维护期,不知前景如何。

七、Gateway 的三大特点

7.1 Route 路由
  路由是构建网关的基本模块,它由ID目标URI,一系列的断言过滤器组成,若是断言为true则匹配该路由
  
7.2 Predicate(断言)
  参考的是 Java8 的 java.util.function.Predicate
  开发人员能够匹配 HTTP 请求中的全部内容(例如请求头或请求参数),若是请求与断言相匹配则进行路由。
  Gateway 内置了 11 个 Predicates Factories 路由策略(断言)工厂类。
  
7.3 Filter(过滤)
  指的是 Spring 框架中 GatewayFilter 的实例,使用过滤器,能够在请求被路由器前或者以后对请求进行修改。
Filter 分 2 类:
(1)31 个 GatewayFilter Factories 网关过滤器工厂类。
(2)10 个 GlobalFilter 全局过滤器接口。

7.4 整体
  Web 请求,经过一系列匹配条件,定位到真正的服务节点。并在这个转发过程的先后,进行一些精细化的控制:
(1)predicate 就是咱们的匹配条件;
(2)filter 能够理解为一个无所不能的拦截器。有了这两个元素在加上目标 uri, 就能够实现一个具体的路由了;

八、Gateway 的工作流程

(1)客户端向Spring Cloud Gateway发出请求。而后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler
(2)Handler 再经过指定的过滤器链来将请求发送到咱们实际的服务执行业务逻辑,而后返回;
(3)过滤器之间用虚线分开是由于过滤器可能会再发送代理请求以前(“pre”)或以后(“post")执行业务逻辑
(4)Filter 在”pre”类型的过滤器能够作参数校验,权限校验,流量监控,日志输出,协议转换等,在”post” 类型的过滤器中能够作响应式内容,响应头的修改,日志的输出,流量监控等有着很是重要的做用;

九、Gateway 应用

1、快速创建 SpringBoot 工程,导入依赖, GateWay 不需要使用 web 模块,它引入的是 WebFlux(类似于 SpringMVC)
完整的 pom 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <artifactId>test-cloud-gateway</artifactId>

    <!--spring boot 父启动器依赖-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--GateWay 网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--引入webflux-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <!--日志依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok工具-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>

        <!--引入Jaxb,开始-->
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.2.10-b140310.1920</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!--引入Jaxb,结束-->

        <!-- Actuator可以帮助你监控和管理Spring Boot应用-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!--链路追踪-->
        <!--<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>-->
    </dependencies>

    <dependencyManagement>
        <!--spring cloud依赖版本管理-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <!--打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、application.yml 配置文件部分内容

server:
  port: 9002
eureka:
  client:
    serviceUrl:
      # eureka server的路径
      #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
  instance:
    #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
    prefer-ip-address: true
    #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
spring:
  application:
  name: lagou-cloud-gateway
  cloud:
    gateway:
      routes: # 路由可以有多个
        - id: service-autodeliver-router # 我们自定义的路由 ID,保持唯一
          #uri: http://127.0.0.1:8096  # 目标服务地址  自动投递微服务(部署多实例)  动态路由:uri配置的应该是一个服务名称,而不应该是一个具体的服务实例的地址
          uri: lb://lagou-service-autodeliver # gateway网关从服务注册中心获取实例信息然后负载后路由
          predicates:  # 断言:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默 认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
            - Path=/autodeliver/**
        - id: service-resume-router      # 我们自定义的路由 ID,保持唯一
          #uri: http://127.0.0.1:8081       # 目标服务地址
          #http://localhost:9002/resume/openstate/1545132
          uri: lb://lagou-service-resume
          predicates:
            - Path=/resume/**
          filters:
            - StripPrefix=1  # 可以去掉uri第一个位置(即resume)之后转发

十、GateWay 路由规则

Spring Cloud GateWay 帮我们内置了很多 Predicates 功能(Gateway 内置了 11 个 Predicates Factories 路由策略(断言)工厂类),实现了各种路由匹配规则 (通过 Header、请求参数等作为条件) 匹配到对应的路由。

(1)时间点后匹配

 spring:
  cloud:
    gateway:
      routes:
       - id: after_route
         uri: https://example.org
         predicates:
          - After=2017-01-20T17:42:47.789-07:00[America/Denver]

(2)时间点前匹配

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

(3)时间区间匹配

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]

(4)指定 Cookie 正则匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

(5)指定 Header 正则匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

(6)请求 Host 匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

(7)请求 Method 匹配指定请求方式

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

(8)请求路径正则匹配

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

(9)请求包含某参数

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

(10)请求包含某参数并且参数值匹配正则表达式

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=red, gree.

(11)远程地址匹配

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

(12)权重匹配

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

参考文章:
https://blog.csdn.net/rain_web/article/details/102469745
https://blog.csdn.net/qq_41402200/article/details/94333830
https://www.shangmayuan.com/a/3951439698fe433f9d6fc4e6.html#Gateway_35
https://blog.csdn.net/rzpy_qifengxiaoyue/article/details/108249285