Gateway:Spring Cloud API閘道器元件
在微服務架構中,一個系統往往由多個微服務組成,而這些服務可能部署在不同機房、不同地區、不同域名下。這種情況下,客戶端(例如瀏覽器、手機、軟體工具等)想要直接請求這些服務,就需要知道它們具體的地址資訊,例如 IP 地址、埠號等。
這種客戶端直接請求服務的方式存在以下問題:
- 當服務數量眾多時,客戶端需要維護大量的服務地址,這對於客戶端來說,是非常繁瑣複雜的。
- 在某些場景下可能會存在跨域請求的問題。
- 身份認證的難度大,每個微服務需要獨立認證。
我們可以通過 API 閘道器來解決這些問題,下面就讓我們來看看什麼是 API 閘道器。
API 閘道器
API 閘道器是一個搭建在客戶端和微服務之間的服務,我們可以在 API 閘道器中處理一些非業務功能的邏輯,例如許可權驗證、監控、快取、請求路由
API 閘道器就像整個微服務系統的門面一樣,是系統對外的唯一入口。有了它,客戶端會先將請求傳送到 API 閘道器,然後由 API 閘道器根據請求的標識資訊將請求轉發到微服務例項。
對於服務數量眾多、複雜度較高、規模比較大的系統來說,使用 API 閘道器具有以下好處:
- 客戶端通過 API 閘道器與微服務互動時,客戶端只需要知道 API 閘道器地址即可,而不需要維護大量的服務地址,簡化了客戶端的開發。
- 客戶端直接與 API 閘道器通訊,能夠減少客戶端與各個服務的互動次數。
- 客戶端與後端的服務耦合度降低。
- 節省流量,提高效能,提升使用者體驗。
- API 閘道器還提供了安全、流控、過濾、快取、計費以及監控
常見的 API 閘道器實現方案主要有以下 5 種:
- Spring Cloud Gateway
- Spring Cloud Netflix Zuul
- Kong
- Nginx+Lua
- Traefik
本節,我們就對 Spring Cloud Gateway 進行詳細介紹。
Spring Cloud Gateway
Spring Cloud Gateway 是 Spring Cloud 團隊基於 Spring 5.0、Spring Boot 2.0 和 Project Reactor 等技術開發的高效能 API 閘道器元件。
Spring Cloud Gateway 旨在提供一種簡單而有效的途徑來發送 API,併為它們提供橫切關注點,例如:安全性,監控/指標和彈性
Spring Cloud Gateway 是基於 WebFlux 框架實現的,而 WebFlux 框架底層則使用了高效能的 Reactor 模式通訊框架 Netty。
Spring Cloud Gateway 核心概念
Spring Cloud GateWay 最主要的功能就是路由轉發,而在定義轉發規則時主要涉及了以下三個核心概念,如下表。
核心概念 | 描述 |
---|---|
Route(路由) | 閘道器最基本的模組。它由一個 ID、一個目標 URI、一組斷言(Predicate)和一組過濾器(Filter)組成。 |
Predicate(斷言) | 路由轉發的判斷條件,我們可以通過 Predicate 對 HTTP 請求進行匹配,例如請求方式、請求路徑、請求頭、引數等,如果請求與斷言匹配成功,則將請求轉發到相應的服務。 |
Filter(過濾器) | 過濾器,我們可以使用它對請求進行攔截和修改,還可以使用它對上文的響應進行再處理。 |
注意:其中 Route 和 Predicate 必須同時宣告。
Spring Cloud Gateway 的特徵
Spring Cloud Gateway 具有以下特性:
- 基於 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 構建。
- 能夠在任意請求屬性上匹配路由。
- predicates(斷言) 和 filters(過濾器)是特定於路由的。
- 集成了 Hystrix 熔斷器。
- 集成了 Spring Cloud DiscoveryClient(服務發現客戶端)。
- 易於編寫斷言和過濾器。
- 能夠限制請求頻率。
- 能夠重寫請求路徑。
Gateway 的工作流程
Spring Cloud Gateway 工作流程如下圖。
Spring Cloud Gateway 工作流程說明如下:
- 客戶端將請求傳送到 Spring Cloud Gateway 上。
- Spring Cloud Gateway 通過 Gateway Handler Mapping 找到與請求相匹配的路由,將其傳送給 Gateway Web Handler。
- Gateway Web Handler 通過指定的過濾器鏈(Filter Chain),將請求轉發到實際的服務節點中,執行業務邏輯返回響應結果。
- 過濾器之間用虛線分開是因為過濾器可能會在轉發請求之前(pre)或之後(post)執行業務邏輯。
- 過濾器(Filter)可以在請求被轉發到服務端前,對請求進行攔截和修改,例如引數校驗、許可權校驗、流量監控、日誌輸出以及協議轉換等。
- 過濾器可以在響應返回客戶端之前,對響應進行攔截和再處理,例如修改響應內容或響應頭、日誌輸出、流量監控等。
- 響應原路返回給客戶端。
總而言之,客戶端傳送到 Spring Cloud Gateway 的請求需要通過一定的匹配條件,才能定位到真正的服務節點。在將請求轉發到服務進行處理的過程前後(pre 和 post),我們還可以對請求和響應進行一些精細化控制。
Predicate 就是路由的匹配條件,而 Filter 就是對請求和響應進行精細化控制的工具。有了這兩個元素,再加上目標 URI,就可以實現一個具體的路由了。
Predicate 斷言
Spring Cloud Gateway 通過 Predicate 斷言來實現 Route 路由的匹配規則。簡單點說,Predicate 是路由轉發的判斷條件,請求只有滿足了 Predicate 的條件,才會被轉發到指定的服務上進行處理。
使用 Predicate 斷言需要注意以下 3 點:
- Route 路由與 Predicate 斷言的對應關係為“一對多”,一個路由可以包含多個不同斷言。
- 一個請求想要轉發到指定的路由上,就必須同時匹配路由上的所有斷言。
- 當一個請求同時滿足多個路由的斷言條件時,請求只會被首個成功匹配的路由轉發。
常見的 Predicate 斷言如下表(假設轉發的 URI 為 http://localhost:8001)。
斷言 | 示例 | 說明 |
---|---|---|
Path | - Path=/dept/list/** | 當請求路徑與 /dept/list/** 匹配時,該請求才能被轉發到 http://localhost:8001 上。 |
Before | - Before=2021-10-20T11:47:34.255+08:00[Asia/Shanghai] | 在 2021 年 10 月 20 日 11 時 47 分 34.255 秒之前的請求,才會被轉發到 http://localhost:8001 上。 |
After | - After=2021-10-20T11:47:34.255+08:00[Asia/Shanghai] | 在 2021 年 10 月 20 日 11 時 47 分 34.255 秒之後的請求,才會被轉發到 http://localhost:8001 上。 |
Between | - Between=2021-10-20T15:18:33.226+08:00[Asia/Shanghai],2021-10-20T15:23:33.226+08:00[Asia/Shanghai] | 在 2021 年 10 月 20 日 15 時 18 分 33.226 秒 到 2021 年 10 月 20 日 15 時 23 分 33.226 秒之間的請求,才會被轉發到 http://localhost:8001 伺服器上。 |
Cookie | - Cookie=name,c.biancheng.net | 攜帶 Cookie 且 Cookie 的內容為 name=c.biancheng.net 的請求,才會被轉發到 http://localhost:8001 上。 |
Header | - Header=X-Request-Id,\d+ | 請求頭上攜帶屬性 X-Request-Id 且屬性值為整數的請求,才會被轉發到 http://localhost:8001 上。 |
Method | - Method=GET | 只有 GET 請求才會被轉發到 http://localhost:8001 上。 |
例項:Gateway:Spring Cloud API閘道器元件
Spring Cloud Gateway 動態路由
預設情況下,Spring Cloud Gateway 會根據服務註冊中心(例如 Eureka Server)中維護的服務列表,以服務名(spring.application.name)作為路徑建立動態路由進行轉發,從而實現動態路由功能。
我們可以在配置檔案中,將 Route 的 uri 地址修改為以下形式。
lb://service-name
以上配置說明如下:
- lb:uri 的協議,表示開啟 Spring Cloud Gateway 的負載均衡功能。
- service-name:服務名,Spring Cloud Gateway 會根據它獲取到具體的微服務地址。
例項:Gateway:Spring Cloud API閘道器元件
Filter 過濾器
通常情況下,出於安全方面的考慮,服務端提供的服務往往都會有一定的校驗邏輯,例如使用者登陸狀態校驗、簽名校驗等。
在微服務架構中,系統由多個微服務組成,所有這些服務都需要這些校驗邏輯,此時我們就可以將這些校驗邏輯寫到 Spring Cloud Gateway 的 Filter 過濾器中。
Filter 的分類
Spring Cloud Gateway 提供了以下兩種型別的過濾器,可以對請求和響應進行精細化控制。
過濾器型別 | 說明 |
---|---|
Pre 型別 | 這種過濾器在請求被轉發到微服務之前可以對請求進行攔截和修改,例如引數校驗、許可權校驗、流量監控、日誌輸出以及協議轉換等操作。 |
Post 型別 | 這種過濾器在微服務對請求做出響應後可以對響應進行攔截和再處理,例如修改響應內容或響應頭、日誌輸出、流量監控等。 |
按照作用範圍劃分,Spring Cloud gateway 的 Filter 可以分為 2 類:
- GatewayFilter:應用在單個路由或者一組路由上的過濾器。
- GlobalFilter:應用在所有的路由上的過濾器。
GatewayFilter 閘道器過濾器
GatewayFilter 是 Spring Cloud Gateway 閘道器中提供的一種應用在單個或一組路由上的過濾器。它可以對單個路由或者一組路由上傳入的請求和傳出響應進行攔截,並實現一些與業務無關的功能,比如登陸狀態校驗、簽名校驗、許可權校驗、日誌輸出、流量監控等。
GatewayFilter 在配置檔案(例如 application.yml)中的寫法與 Predicate 類似,格式如下。
spring:
cloud:
gateway:
routes:
- id: xxxx
uri: xxxx
predicates:
- Path=xxxx
filters:
- AddRequestParameter=X-Request-Id,1024 #過濾器工廠會在匹配的請求頭加上一對請求頭,名稱為 X-Request-Id 值為 1024
- PrefixPath=/dept #在請求路徑前面加上 /dept
……
Spring Cloud Gateway 內建了多達 31 種 GatewayFilter,下表中列舉了幾種常用的閘道器過濾器及其使用示例。
路由過濾器 | 描述 | 引數 | 使用示例 |
---|---|---|---|
AddRequestHeader | 攔截傳入的請求,並在請求上新增一個指定的請求頭引數。 | name:需要新增的請求頭引數的 key; value:需要新增的請求頭引數的 value。 |
- AddRequestHeader=my-request-header,1024 |
AddRequestParameter | 攔截傳入的請求,並在請求上新增一個指定的請求引數。 | name:需要新增的請求引數的 key; value:需要新增的請求引數的 value。 |
- AddRequestParameter=my-request-param,c.biancheng.net |
AddResponseHeader | 攔截響應,並在響應上新增一個指定的響應頭引數。 | name:需要新增的響應頭的 key; value:需要新增的響應頭的 value。 |
- AddResponseHeader=my-response-header,c.biancheng.net |
PrefixPath | 攔截傳入的請求,並在請求路徑增加一個指定的字首。 | prefix:需要增加的路徑字首。 | - PrefixPath=/consumer |
PreserveHostHeader | 轉發請求時,保持客戶端的 Host 資訊不變,然後將它傳遞到提供具體服務的微服務中。 | 無 | - PreserveHostHeader |
RemoveRequestHeader | 移除請求頭中指定的引數。 | name:需要移除的請求頭的 key。 | - RemoveRequestHeader=my-request-header |
RemoveResponseHeader | 移除響應頭中指定的引數。 | name:需要移除的響應頭。 | - RemoveResponseHeader=my-response-header |
RemoveRequestParameter | 移除指定的請求引數。 | name:需要移除的請求引數。 | - RemoveRequestParameter=my-request-param |
RequestSize | 配置請求體的大小,當請求體過大時,將會返回 413 Payload Too Large。 | maxSize:請求體的大小。 | - name: RequestSize args: maxSize: 5000000 |
例項:Gateway:Spring Cloud API閘道器元件
GlobalFilter 全域性過濾器
GlobalFilter 是一種作用於所有的路由上的全域性過濾器,通過它,我們可以實現一些統一化的業務功能,例如許可權認證、IP 訪問限制等。當某個請求被路由匹配時,那麼所有的 GlobalFilter 會和該路由自身配置的 GatewayFilter 組合成一個過濾器鏈。
Spring Cloud Gateway 為我們提供了多種預設的 GlobalFilter,例如轉發、路由、負載均衡等相關的全域性過濾器。但在實際的專案開發中,通常我們都會自定義一些自己的 GlobalFilter 全域性過濾器以滿足我們自身的業務需求,而很少直接使用 Spring Cloud Config 提供這些預設的 GlobalFilter。
關於預設的全域性過濾器的詳細內容,請參考 Spring Cloud 官網。
例項:Gateway:Spring Cloud API閘道器元件