天天看点

Spring Cloud Alibaba:Gateway网关 & 路由断言工厂

Spring Cloud Gateway

该项目提供了一个用于在​

​Spring Webflux​

​​之上构建​

​API​

​​网关的库。​

​Spring Cloud Gateway​

​​旨在提供一种简单而有效的方式来路由到​

​API​

​并为它们提供交叉关注点,例如:安全、监控和弹性。

​Spring Cloud Gateway​

​​需要​

​Spring Boot​

​​和​

​Spring Webflux​

​​提供的​

​Netty​

​​运行时环境。它不适用于传统的​

​Servlet​

​​容器或将应用构建为​

​WAR​

​​包。如果强制使用传统的​

​Servlet​

​来处理请求:

Spring Cloud Alibaba:Gateway网关 & 路由断言工厂

启动应用时会报错:

Spring Cloud Alibaba:Gateway网关 & 路由断言工厂

Spring Webflux is missing from the classpath, which is required for Spring Cloud Gateway at this time. Please add spring-boot-starter-webflux dependency.

Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.

在​

​classpath​

​​中缺少​

​Spring Webflux​

​​,此时​

​Spring Cloud Gateway​

​​需要它。请添加​

​spring-boot-starter-webflux​

​​依赖项。

在​​

​classpath​

​​上发现​

​Spring MVC​

​​,此时与​

​Spring Cloud Gateway​

​​不兼容。请删除​

​spring-boot-starter-web​

​依赖项。

重要概念:

  • 路由(Route):路由是网关的基本模块。它由​

    ​ID​

    ​、目标​

    ​URI​

    ​、​

    ​Predicate​

    ​集合和​

    ​Filter​

    ​集合定义。如果聚合​

    ​Predicate​

    ​为真,则匹配路由。
  • 断言(Predicate):输入类型是​

    ​Spring Framework ServerWebExchange​

    ​。它允许开发人员匹配来自​

    ​HTTP​

    ​请求的任何内容,例如请求头或请求参数。
  • Spring Cloud Alibaba:Gateway网关 & 路由断言工厂
  • 代码注释翻译插件​

    ​Translation​

    ​:
  • Spring Cloud Alibaba:Gateway网关 & 路由断言工厂
  • 过滤器(Filter):使用特定工厂构建的​

    ​Spring Framework GatewayFilter​

    ​实例,可以在发送代理请求之前或之后修改请求和响应。
  • Spring Cloud Alibaba:Gateway网关 & 路由断言工厂

​Spring Cloud Gateway​

​特性:

  • 基于​

    ​Spring Framework 5​

    ​​、​

    ​Project Reactor​

    ​​和​

    ​Spring Boot 2.0​

    ​。
  • 能够匹配任何请求属性的路由。
  • 特定于路由的断言和过滤器。
  • 集成​

    ​Circuit Breaker​

    ​。
  • 集成​

    ​Spring Cloud DiscoveryClient​

    ​。
  • 断言和过滤器易于编写。
  • 请求速率限制。
  • 路径重写。

​Spring Cloud Gateway​

​工作方式(图来自官网):

Spring Cloud Alibaba:Gateway网关 & 路由断言工厂

客户端向​

​Spring Cloud Gateway​

​​发出请求。如果​

​Gateway Handler Mapping​

​​确定请求与路由匹配,则将其发送到​

​Gateway Web Handler​

​​。此处理程序通过特定于请求的过滤器链,将请求转换成代理请求。过滤器被虚线分隔的原因是过滤器可能在发送代理请求之前或之后执行逻辑。执行所有​

​pre​

​​过滤器逻辑(作用于请求),然后发出代理请求。代理请求得到响应后,执行所有​

​post​

​过滤器逻辑(作用于响应)。

搭建工程

一个父​

​module​

​​和两个子​

​module​

​​(​

​nacos module​

​​提供服务,​

​gateway module​

​实现网关)。

父​

​module​

​​的​

​pom.xml​

​:

<?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>

    <groupId>com.kaven</groupId>
    <artifactId>alibaba</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <description>Spring Cloud Alibaba</description>
    <modules>
        <module>nacos</module>
        <module>gateway</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring-cloud-version>Hoxton.SR9</spring-cloud-version>
        <spring-cloud-alibaba-version>2.2.6.RELEASE</spring-cloud-alibaba-version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <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>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba-version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>      

nacos module

​pom.xml​

​:

<?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>
    
    <parent>
        <groupId>com.kaven</groupId>
        <artifactId>alibaba</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>nacos</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <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-web</artifactId>
        </dependency>
    </dependencies>
</project>      

​application.yml​

​:

server:
  port: 8080

spring:
  application:
    name: nacos
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.197:9000      

接口定义:

package com.kaven.alibaba.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MessageController {

    @GetMapping("/message")
    public String getMessage() {
        return "hello kaven, this is nacos";
    }
}      

启动类:

package com.kaven.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class NacosApplication {
    public static void main(String[] args) {
        SpringApplication.run(NacosApplication.class);
    }
}      

gateway module

​pom.xml​

​:

<?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>
    
    <parent>
        <groupId>com.kaven</groupId>
        <artifactId>alibaba</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>gateway</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>      

​application.yml​

​:

server:
  port: 8085

spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 192.168.1.197:9000
    gateway:
      routes:
        - id: nacos
          uri: http://localhost:8080
          predicates:
            - Path=/message      

启动类:

package com.kaven.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}      

启动这两个​

​module​

​​,​

​Nacos​

​的服务列表就会出现这两个服务。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

路由断言工厂

​Spring Cloud Gateway​

​​匹配路由作为​

​Spring Webflux HandlerMapping​

​​基础功能的一部分。​

​Spring Cloud Gateway​

​​包括许多内置的路由断言工厂,这些路由断言工厂都匹配​

​HTTP​

​​请求的不同属性,可以通过逻辑​

​and​

​来组合多个路由断言工厂。

After

​After​

​路由断言工厂接受一个日期时间,该断言匹配该日期时间之后的请求。生成这个日期时间的示例代码如下所示:

package com.kaven.alibaba;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Test {
    public static void main(String[] args) {

        ZonedDateTime nowDateTime = LocalDateTime.now()
                .atZone(ZoneId.systemDefault());
        System.out.println(nowDateTime);

        ZonedDateTime updateDateTime = LocalDateTime.now()
                .plusMonths(1)
                .minusDays(1).minusHours(1)
                .atZone(ZoneId.systemDefault());
        System.out.println(updateDateTime);

        ZonedDateTime hardCodeDateTime = LocalDateTime.
                of(2021, 12, 17, 18, 54, 17, 0)
                .atZone(ZoneId.systemDefault());
        System.out.println(hardCodeDateTime);
    }
}      

输出:

2021-12-17T19:15:40.297+08:00[Asia/Shanghai]
2022-01-16T18:15:40.298+08:00[Asia/Shanghai]
2021-12-17T18:54:17+08:00[Asia/Shanghai]      

表示年、月、日,时、分、秒、纳秒(不是毫秒)以及时区。

- After=2021-12-17T19:30:00+08:00[Asia/Shanghai]      

不在指定日期时间之后访问接口会直接响应​

​404​

​。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

指定日期时间之后,接口就可以正常访问了。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

Before

​Before​

​​路由断言工厂接受一个日期时间。此断言匹配在该日期时间之前发生的请求。由于和​

​After​

​路由断言工厂类似,这里就不再演示。

- Before=2021-12-17T19:38:00.129+08:00[Asia/Shanghai]      

Between

​Between​

​​路由断言工厂接受两个日期时间。此断言匹配发生在​

​datetime1​

​​之后和​

​datetime2​

​​之前的请求。​

​Between​

​路由断言工厂与上面两种路由断言工厂类似,这里也不再演示。

- Between=2021-12-17T19:38:00.129+08:00[Asia/Shanghai],2021-12-17T19:42:00.129+08:00[Asia/Shanghai]      

​datetime1​

​​参数指定的日期时间必须在​

​datetime2​

​参数指定的日期时间之前。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

Cookie

​Cookie​

​​路由断言工厂接受两个参数,即​

​Cookie​

​​名称和一个正则表达式。此断言匹配具有给定名称且值与正则表达式匹配的​

​Cookie​

​的请求。

- Cookie=kaven,*kaven*      

使用​

​Postman​

​​来测试,请求没有​

​Cookie​

​:

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

请求没有匹配的​

​Cookie​

​:

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

请求有匹配的​

​Cookie​

​:

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

路由的断言配置是一个​

​List​

​​类型,因此可以配置多个断言,当聚合断言(聚合在信息科学中是指对有关的数据进行内容挑选、分析、归类,最后分析得到人们想要的结果,看完这篇博客就应该理解这里使用聚合这个词是比较合适的,因为断言中有逻辑​

​and​

​​也有逻辑​

​or​

​)为真时,才匹配路由。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
- Cookie=username,.*kaven.*
            - Cookie=password,.*kaven.*      

请求有所有匹配的​

​Cookie​

​:

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

请求只有部分匹配的​

​Cookie​

​:

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

所以,当断言列表中有互相矛盾的断言时,该路由就不可能匹配成功。

Header

​Header​

​​路由断言工厂接受两个参数,​

​Header​

​​名称和一个正则表达式。此断言与具有给定名称且值与正则表达式匹配的​

​Header​

​的请求匹配。

- Header=gateway,.*kaven.*      
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

Host

​Host​

​​路由断言工厂接受一个参数:​

​Host​

​​模式列表。该模式是一个​

​Ant​

​​风格(请求路径的一种匹配方式)的模式,以​

​.​

​​作为分隔符。此断言匹配​

​Host​

​​满足列表中任意模式的请求。​

​Ant​

​通配符如下图所示:

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
- Host=**.kaven.com,**.kaven.top      
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

​*​

​​和​

​?​

​通配符这里就不演示了。

Method

​Method​

​​路由断言工厂接受一个参数:要匹配的​

​HTTP​

​方法。

- Method=GET      
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

为了演示必须要满足指定的​

​HTTP​

​​方法才能匹配路由,在​

​nacos module​

​​中增加一个​

​POST​

​接口。

@PostMapping("/message")
    public String updateMessage(String message) {
        return message;
    }      

​POST​

​​方法不匹配指定的​

​GET​

​​方法,直接响应​

​404​

​。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

指定多个要匹配的​

​HTTP​

​方法,满足其中一个就可以匹配路由。

- Method=GET,POST      
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

Path

​Path​

​​路由断言工厂接受两个参数:一个​

​Spring PathMatcher​

​​模式列表和一个可选的标志​

​matchOptionalTrailingSeparator​

​​(此参数为​

​true​

​​时,如果模式没有尾部斜杠,请求路径有尾部斜杠也能成功匹配,否则不能成功匹配,该参数默认为​

​true​

​​)。该模式匹配也符合​

​Ant​

​​风格,​

​Ant​

​通配符如下图所示:

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

为了方便演示,在​

​nacos module​

​中增加几个接口。

@GetMapping("/kaven")
    public String kaven() {
        return "hello kaven";
    }

    @GetMapping("/kaven/1")
    public String kaven1() {
        return "hello kaven, this is 1";
    }


    @GetMapping("/kaven/1/2")
    public String kaven1_2() {
        return "hello kaven, this is 1/2";
    }
    
    @GetMapping("/itkaven")
    public String itkaven() {
        return "hello itkaven";
    }

    @GetMapping("/itkaven/1")
    public String itkaven1() {
        return "hello itkaven, this is 1";
    }
    
    @GetMapping("/itkaven/1/2")
    public String itkaven1_2() {
        return "hello itkaven, this is 1/2";
    }      
- Path=/kaven/{path},/itkaven/**      

​/itkaven/**​

​​表示路径是否以​

​/itkaven​

​​开头(​

​**​

​​表示后面有​

​0​

​​个或者多个单位路径,​

​*​

​​和​

​?​

​通配符这里就不演示了),如果是则匹配。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

​/kaven/{path}​

​​表示路径是否以​

​/kaven​

​​开头并且后面有​

​1​

​个单位路径,如果是则匹配。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

​/kaven/2​

​​这个路径是匹配的,但​

​nacos module​

​​中没有这个接口,仔细观察可以知道,​

​/kaven/2​

​​路径的响应和​

​/kaven​

​​路径的响应是不一样的(返回响应的主体不同,前者的响应是​

​nacos module​

​​返回的,而后者的响应是​

​gateway module​

​返回的)。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

路由的断言配置修改成如下所示,表示路径是否以​

​/kaven​

​​开头并且后面有​

​2​

​个单位路径(以此类推)。

- Path=/kaven/{path1}/{path2}      

​/kaven/1/2​

​路径就可以匹配成功了。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

​matchOptionalTrailingSeparator​

​​为​

​false​

​。

- Path=/kaven/{path},false      

路径有尾部斜杠不能成功匹配。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

​matchOptionalTrailingSeparator​

​​为​

​true​

​(默认)。

- Path=/kaven/{path},true      

路径有尾部斜杠也能成功匹配。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

如果模式有尾部斜杠,请求路径也必须有尾部斜杠,此时​

​matchOptionalTrailingSeparator​

​参数的值就不起作用了。

Query

​Query​

​​路由断言工厂接受两个参数:必需的​

​param​

​​和可选的​

​regexp​

​。

- Query=kaven      
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
- Query=kaven,.*itkaven.*      
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂

RemoteAddr

​RemoteAddr​

​​路由断言工厂采用​

​CIDR​

​​(用于解释​

​IP​

​​地址的标准)表示的​

​IPv4​

​​或​

​IPv6​

​​字符串列表(最少​

​1​

​​个),例如​

​192.168.1.199/24​

​​(其中​

​192.168.1.199​

​​是​

​IP​

​​地址,​

​24​

​是子网掩码位数)。

- RemoteAddr=192.168.1.1/24      

在虚拟机中发送请求,虚拟机​

​IP​

​地址匹配。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂
- RemoteAddr=192.168.2.1/24      

虚拟机​

​IP​

​地址已经不匹配了。

Spring Cloud Alibaba:Gateway网关 &amp; 路由断言工厂