微服務網關——實現篇
在《微服務網關——需求篇 》中,我們討論了微服務網關的需求;而在《微服務網關——設計篇 》中,我們討論了微服務網關的設計。本文將對微服務網關進行實現。
對於網關的開發,完全自研的難度比較大,特別是 IO 的處理。考慮到目前市面上有比較多的成熟框架,可以基於成熟的開源框架,進行二次開發。
對於 Java 來說,目前 Spring 提供了 SpringCloud Gateway(下面簡稱 SG),SG 基於 SpringWebFlux,底層默認使用的是 Netty 框架,支持高併發請求,同時提供了一些默認的組件,支持路由、限流等功能。可以基於 SG 進行二次開發。
如果對網關有特別多的自定義需求,可以直接基於 Netty 來進行更徹底的二次開發,適配自身系統的個性化需求。
本文將基於 SG 來討論網關的實現。
基於 SpringCloud Gateway 的網關設計
這裏針對前面的需求,梳理如何基於 SpringCloud Gateway 來實現。關於 SpringCloud Gateway 本身的設計和實現,會新開一篇專門討論。
SpringCloud Gateway 整體架構
SpringCloud Gateway 基於 SpringWebFlux,整體架構如下圖所示:
SG 定義了幾個概念:
- 路由 (Route):路由是網關的基本構成單元。它由一個 ID、一個目標 URL、一組謂詞以及一組過濾器組成。當謂詞判定爲 true 時,表示請求與對應路由匹配
- 謂詞 (Predicate):Java8 函數式謂詞。輸入參數是 Spring 框架封裝的 ServerWebExchange 對象。開發人員可以基於此對象來匹配 HTTP 請求的任意內容,比如請求頭或請求參數
- 過濾器 (Filter):由特定工廠類構造的一組 Spring 框架提供的 GatewayFilter 對象。過濾器可以在請求或響應被處理前 / 後對其進行修改。
SG 處理請求的大致流程如下:
GatewayHandlerMapping 判定對應的請求是否匹配某個路由。如果匹配到某個路由,則將請求交給 GatewayWebHandler 處理。Handler 調用一個 Filter 鏈來處理這個請求,具體執行流程如下:
- 首先,會執行「pre」過濾器的邏輯
- 然後執行請求處理邏輯
- 最後再執行「post」過濾器的邏輯
SG 提供了 GatewayFilter 和 GlobalFilter 兩種類型的過濾器,從名字可以看出 GlobalFilter 是對全局生效的,而 GatewayFilter 是對特定請求生效的。
注意:這裏的 GlobalFilter 是針對所有匹配了 GatewayHandlerMapping 的請求生效,而不是對所有進入網關的請求生效。
假設,你在網關中編寫了一個 Controller,但是路由配置中並沒有匹配該 Controller 的路徑,那麼針對該 Controller 的請求並不會觸發任何 GlobalFilter。
SG 針對一個請求的完整流程如下圖所示:
可以看到,SG 的擴展是基於一個個的 Filter 來實現的。前面提到的大部分需求也完全可以基於 Filter 去實現,包括但不限於路由、負載均衡、認證授權、過載保護、緩存、服務重試、日誌記錄等。
這裏僅以限流爲例,來說明 SG 的擴展邏輯。其它實現請自行閱讀源碼,或關注後續內容。
限流實現
SG 提供了
RequestRateLimiterGatewayFilterFactory 過濾器支持限流,同時也支持基於 histrix 的過載保護,直接集成使用即可,具體請見 histrix 文檔。
這裏以
RequestRateLimiterGatewayFilterFactory 爲例來分析 SG 如何基於攔截器來實現 j 限流的。SG 中的限流是針對每個路由來單獨定義的,配置內容如下:
上面的配置,配置了一個路由:
- id 爲 test_route
- 路由地址到 lb://test。前面的 lb:// 表示支持負載均衡,後面的 test 是服務名稱
- predicates 是謂詞,表示當請求以 / test 開頭時,此路由生效
- 同時配置了一個限流攔截器,包括名稱和參數。基於此配置通過 RequestRateLimiterGatewayFilterFactory 來構建對應的 Filter。其中的參數是用來配置請求速率的,即每秒允許 10 個請求,允許短時間(這裏是 1 秒)湧入 20 個請求。
上面的配置會被構建爲一個 RouteLocator 實例,該類根據配置構建 Route、Predicates、Filter 等實例。對於 Filter 來說,
- 在創建 RouteLocator 實例時,已創建的 GatewayFilterFactory 實例列表會被作爲參數傳入
- list 被轉換爲 map,key 爲去除了 GatewayFilterFactory 後綴名的 Filter 類名(與配置文件中的 Filter 的配置匹配),value 是對應的 GatewayFilterFactory 實例
- 在構建 Route 實例時,會根據配置文件中的 Filter 的配置獲取 Filter 實例設置進 Route 實例中,提供給後續流程使用
RequestRateLimiterGatewayFilterFactory 核心代碼如下所示:
- 1 處,獲取配置參數。這裏的 KeyResolver 是對獲取請求的 key 邏輯的抽象。比如其中的一個實現是 PrincipalNameKeyResolver,它從 ServerWebExchange 中獲取 Principal 對象,並將 Principal.getName() 的返回值作爲當前請求的 key,如果值相同,則認定爲同一個請求。
- 2 處,構建匿名 GatewayFilter
- 3 處,基於限流器來處理限流邏輯
官方提供的分佈式限流方案是基於 redis 實現。
核心邏輯在 RedisRateLimiter 的 isAllowed() 方法中:
- 1 處,獲取參數(上面已經解釋過了)
- 2 處,構建 lua 參數,執行 lua 腳本
- 3 處,根據返回值判定是否允許訪問
lua 腳本就不貼出來了,使用的是令牌桶算法。
自定義攔截器
從上面的梳理,可以看出,要編寫一個自定義的 GatewayFilter 的流程如下:
- 編寫一個 FilterFactory 類繼承 AbstractGatewayFilterFactory,這個類實現了 GatewayFilterFactory 接口,該接口提供了一個名叫 name 的默認方法,用於提供 Factory 的 name,作爲獲取對應實例的名稱。邏輯就是將對應 FilterFactory 類名後的「GatewayFilterFactory」去掉,例如上面的 RequestRateLimiterGatewayFilterFactory,對應的配置名稱就是 RequestRateLimiter。
- 在覆寫的 apply 方法中構建對應的 Filter,可以是直接返回匿名 GatewayFilter,或返回對應 Filter 的實例
- 如果返回的是 Filter 的實例,則需要編寫一個實現了 GatewayFilter 的 Filter 類,實現其 filter 方法
- 然後將 FilterFactory、實現的 Filter(如果有的話)配置給 Spring 管理
- 最後,就可以在配置文件中針對需要的路由配置對應的 Filter 了
聚合服務
SG 基於 SpringWebFlux,支持編寫異步 RESTful 接口,同時提供 WebClient 異步 REST 客戶端來實現聚合服務的編寫。不過由於是編碼的形式,所以需要發佈網關。對於集羣部署的情況下,在可用性要求沒有特別嚴格的情況下,此方式可以接受。
如果聚合服務較多且發佈頻繁,可以獨立出聚合服務層。即基於 SpringWebFlux 構建微服務,使用 WebClient 來整合獨立微服務的邏輯,網關路由至對應的服務即可。
關於 WebClient 的使用方法請自行搜索,這裏就不贅述了。
非功能性需求
SG 本身就基本符合前面提到的非功能性需求
-
基於 SpringWebFlux 的非阻塞 IO 模型,支持高併發。
-
基於分佈式配置服務進行動態配置
-
基於 GatewayFilter 和 GlobalFilter 進行擴展
-
可以接入 PinPoint 的鏈路監控以及基於 ELK 的日誌監控平臺
主要注意保證無狀態設計!
總結
本文及之前的「微服務網關——需求篇」和「 微服務網關——設計篇 」對微服務網關進行了梳理。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://www.toutiao.com/i6903086075166671373/