為什麼引入 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 同時存在於同一個路由時,請求必須同時滿足所有的條件才被這個路由匹配。
一個請求滿足多個路由的斷言條件時,請求只會被首個成功匹配的路由轉發
結語
本篇到此結束,歡迎大家關注公眾號【當我遇上你】。