天天看點

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; 路由斷言工廠