spring-cloud-gateway靜態路由

當我遇上你發表於2020-04-04

為什麼引入 API 閘道器

使用 API 閘道器後的優點如下:

  • 易於監控。可以在閘道器收集監控資料並將其推送到外部系統進行分析。
  • 易於認證。可以在閘道器上進行認證,然後再將請求轉發到後端的微服務,而無須在每個微服務中進行認證。
  • 減少了客戶端與各個微服務之間的互動次數。

基本環境見 spring-cloud-gateway 簡介, 專案中 provider1 的 maven 配置在此做下更正

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">
    <parent>
        <artifactId>spring-cloud-learning</artifactId>
        <groupId>cn.idea360</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>idc-provider1</artifactId>

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

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

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
複製程式碼

靜態路由基本實現

靜態路由比較簡單,這裡做簡單實現。

更改路由模組 application.yml 配置,更改後的配置檔案如下:

server:
  port: 2000
spring:
  application:
    name: idc-gateway
  redis:
    host: localhost
    port: 6379
    timeout: 6000ms  # 連線超時時長(毫秒)
    jedis:
      pool:
        max-active: 1000  # 連線池最大連線數(使用負值表示沒有限制)
        max-wait: -1ms      # 連線池最大阻塞等待時間(使用負值表示沒有限制)
        max-idle: 10      # 連線池中的最大空閒連線
        min-idle: 5       # 連線池中的最小空閒連線
  cloud:
    consul:
      host: localhost
      port: 8500
    gateway:
      discovery:
        locator:
          enabled: true # gateway可以通過開啟以下配置來開啟根據服務的serviceId來匹配路由,預設是大寫
      routes:
        - id: provider1  # 路由 ID,保持唯一
          uri: lb://idc-provider1 # uri指目標服務地址,lb代表從註冊中心獲取服務
          predicates: # 路由條件。Predicate 接受一個輸入引數,返回一個布林值結果。該介面包含多種預設方法來將 Predicate 組合成其他複雜的邏輯(比如:與,或,非)
            - Path=/p/**
          filters:
            - StripPrefix=1 # 過濾器StripPrefix,作用是去掉請求路徑的最前面n個部分擷取掉。StripPrefix=1就代表擷取路徑的個數為1,比如前端過來請求/test/good/1/view,匹配成功後,路由到後端的請求路徑就會變成http://localhost:8888/good/1/view
        - id: provider2  # 路由 ID,保持唯一
          uri: lb://idc-provider2 # uri指目標服務地址,lb代表從註冊中心獲取服務
          predicates: # 路由條件。Predicate 接受一個輸入引數,返回一個布林值結果。該介面包含多種預設方法來將 Predicate 組合成其他複雜的邏輯(比如:與,或,非)
            - Path=/p2/**
          filters:
            - StripPrefix=1 # 過濾器StripPrefix,作用是去掉請求路徑的最前面n個部分擷取掉。StripPrefix=1就代表擷取路徑的個數為1,比如前端過來請求/test/good/1/view,匹配成功後,路由到後端的請求路徑就會變成http://localhost:8888/good/1/view
複製程式碼

測試

執行以下請求測試路由 1

curl http://localhost:2000/p/provider1/1
複製程式碼

結果 返回 2001 。說明成功路由到了 provider1 服務。

執行以下請求測試路由 2

curl http://localhost:2000/p2/provider2
複製程式碼

返回 {"port":"2002"} 。說明成功路由到了 provider2 服務。

Predicate 斷言條件

1 通過請求引數匹配

Query Route Predicate 支援傳入兩個引數,一個是屬性名一個為屬性值,屬性值可以是正規表示式。

...
        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p/**
            - Query=username
          filters:
            - StripPrefix=1
複製程式碼

這樣配置,只要請求中包含 username 屬性的引數即可匹配路由。否則 404

測試

curl http://localhost:2000/p/provider1/1?username=admin
複製程式碼

經過測試發現只要請求彙總帶有 username 引數即會匹配路由,不帶 username 引數則不會匹配。

還可以將 Query 的值以鍵值對的方式進行配置,這樣在請求過來時會對屬性值和正則進行匹配,匹配上才會走路由。

        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p/**
            - Query=username, ad.
          filters:
            - StripPrefix=1
複製程式碼

這樣只要當請求中包含 username 屬性並且引數值是以 admin 開頭的長度為三位的字串才會進行匹配和路由。

測試

curl http://localhost:2000/p/provider1/1?username=adm
複製程式碼

測試可以返回頁面程式碼,將 username 的屬性值改為 admin 再次訪問就會報 404,證明路由需要匹配正規表示式才會進行路由。

2 通過 Header 屬性匹配

Header Route Predicate 和 Cookie Route Predicate 一樣,也是接收 2 個引數,一個 header 中屬性名稱和一個正規表示式,這個屬性值和正規表示式匹配則執行。

        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p/**
            - Header=token, \d+
          filters:
            - StripPrefix=1
複製程式碼

測試

curl http://localhost:2000/p/provider1/1 -H "token:11"
複製程式碼

則返回頁面程式碼證明匹配成功。將引數-H "token:11"改為-H "token:spring"再次執行時返回 404 證明沒有匹配。

3 通過 Cookie 匹配

Cookie Route Predicate 可以接收兩個引數,一個是 Cookie name ,一個是正規表示式,路由規則會通過獲取對應的 Cookie name 值和正規表示式去匹配,如果匹配上就會執行路由,如果沒有匹配上則不執行。

        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p/**
            - Cookie=sessionId, test
          filters:
            - StripPrefix=1
複製程式碼

使用 curl 測試,命令列輸入:

curl http://localhost:2000/p/provider1/1 --cookie "sessionId=test"
複製程式碼

則會返回頁面程式碼,如果去掉--cookie "sessionId=test",後臺會報 404 錯誤。

4 通過 Host 匹配

Host Route Predicate 接收一組引數,一組匹配的域名列表,這個模板是一個 ant 分隔的模板,用.號作為分隔符。它通過引數中的主機地址作為匹配規則。

        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p/**
            - Host=**.baidu.com
          filters:
            - StripPrefix=1
複製程式碼

使用 curl 測試,命令列輸入:

curl http://localhost:2000/p/provider1/1 -H "Host: www.baidu.com"
curl http://localhost:2000/p/provider1/1 -H "Host: md.baidu.com"
複製程式碼

經測試以上兩種 host 均可匹配到 host_route 路由,去掉 host 引數則會報 404 錯誤。

5 通過請求方式匹配

可以通過是 POST、GET、PUT、DELETE 等不同的請求方式來進行路由。

        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p/**
            - Method=GET
          filters:
            - StripPrefix=1
複製程式碼

使用 curl 測試,命令列輸入:

curl http://localhost:2000/p/provider1/1
複製程式碼

測試返回頁面程式碼,證明匹配到路由,我們再以 POST 的方式請求測試。

curl -X POST http://localhost:2000/p/provider1/1
複製程式碼

返回 404 沒有找到,證明沒有匹配上路由

6 通過請求路徑匹配

Path Route Predicate 接收一個匹配路徑的引數來判斷是否走路由。

        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p/{segment}
            - Method=POST
          filters:
            - StripPrefix=1
複製程式碼

如果請求路徑符合要求,則此路由將匹配,例如:/foo/1 或者 /foo/bar。

7 通過請求 ip 地址進行匹配

Predicate 也支援通過設定某個 ip 區間號段的請求才會路由,RemoteAddr Route Predicate 接受 cidr 符號(IPv4 或 IPv6 )字串的列表(最小大小為 1),例如 192.168.124.5/16 (其中 192.168.124.5 是 IP 地址,16 是子網掩碼)。

        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p/**
            - RemoteAddr=192.168.124.5/16
          filters:
            - StripPrefix=1
複製程式碼

可以將此地址設定為本機的 ip 地址進行測試。

curl http://192.168.124.5:2000/p/provider1/1
複製程式碼

如果請求的遠端地址是 192.168.124.5,則此路由將匹配。

8 組合使用

各種 Predicates 同時存在於同一個路由時,請求必須同時滿足所有的條件才被這個路由匹配。

一個請求滿足多個路由的斷言條件時,請求只會被首個成功匹配的路由轉發

結語

本篇到此結束,歡迎大家關注公眾號【當我遇上你】。

相關文章