微服務網關
在微服務架構中,隨着微服務的拆分,這些微服務不可能同時提供對外服務,這樣就需要一個網關系統,承接外網的流量。有了API 網關,各個 API 服務提供團隊可以專注自己的業務邏輯處理,而 API 網關則更專注於安全、流量、路由等問題。
我們先來看一下微服務網關主要提供哪些功能。
統一流量接入:提供統一的流量入口,這樣就可以由統一的入口管理流量,設置各種策略,比如統一的 Token 認證等。
業務聚合:在具體的業務中,經常需要聚合多個服務的結果集,返回給客戶端,這個時候可以由 API 網關聚合數據然后返回給客戶端。
協議轉換:一般入口層,多使用 HTTP 協議的 RESTful 接口,而后端服務的協議可能有多種,比如 gRPC、Thrift 等。
中間件策略:設置統一的中間件策略層,可以在這一層做一些限流熔斷、南北向流量的服務治理功能。
安全認證:一些 Token 認證功能,比如數據是否被篡改的認證,可以在 API 網關中做,這也是經常用到的功能。
證書管理:隨着對外網安全性能要求的增高,現在基本上都要對外提供 HTTPS 的服務,以保證數據不會被劫持、篡改等問題,在這一層做證書對內網的拆卸非常合適。由一個統一的入口管理接口,降低了證書更換時的復雜度。
常用微服務網關介紹
Kong:幾乎是目前最流行的微服務網關,內置了多種網關所需要的功能,后面我們將詳細介紹。
Spring Cloud Zuul:作為 Spring Cloud 的一部分,提供了微服務所需要的網關的大部分功能,最大的優勢是對 Java 系統友好。嚴格來說Zuul 更像是一個微服務網關的框架,而不是獨立的產品。
Spring Cloud Gateway:Spring Cloud Gateway 是 Spring 官方自己開發的一款 API 網關。性能比Zuul更加優越。
雙重網關(系統網關和業務網關)
隨着業務進一步發展,單一的網關系統已經不能滿足組織架構和業務的發展了,所以在實際生產中,多采用雙重網關結構。
這種結構由多個系統網關和多個業務聚合網關組成,這樣配合是因為系統網關多由常見的 Nginx 系網關構成,使用 Nginx 系的網關對運維友好,方便運維做一些監控、日志、安全策略;但往往對研發人員不太友好,不方便擴展。所以后來增加了業務網關,用於服務的聚合,這層網關因為需要做一些業務的聚合策略,平時改動會比較多。
如下圖所示,兩個網關相互配合,既方便了運維維護,也方便了研發人員擴展,達到某種平衡。
另外,隨着單一業務的發展,我們也會像下圖一樣拆分出多個獨立的網關,這些網關會根據業務維度拆分,比如分為用戶系統網關和內容系統網關。這樣拆分的目的,是防止由單一網關的故障帶來全站的不可用,從而達到降低故障影響面的效果。
微服務網關 Kong
開源網關 Kong是一個輕量級的 API 網關,基於 OpenResty + Lua 開發,提供了豐富的功能以及高度靈活的擴展性,可以通過插件的方式擴展 Kong 的功能。
Kong 作為微服務網關,最大的優勢就是抽象出了微服務網關的一套模型,而不需要像 OpenResty 那樣手動配置。
下面我們看一下這套模型中的各個名詞的含義。
service:服務對應的后端的微服務和 API。一般情況下把一組 API 集合成為一個服務,也就是我們后端的微服務。
router:路由指的是流量到達 API 網關后,如何找到后端對應的服務。針對不同的流量實現多種路由策略。
Admin API:Kong 中的 RESTful 的內部 API 接口。 API 接口可以在集群的任意節點運行,最終同步到所有集群的節點。用於管理 Kong 的各種行為,比如添加 service、router 等。
Plugins:Kong 的插件系統。利用插件系統,我們添加新功能就不需要改動 Kong 的核心代碼。插件系統自帶足夠多的功能,比如認證、限流、日志、Metrics 等。
Load Balancing:Kong提供了兩種負載均衡方式,DNS 和內置的負載均衡器。利用 DNS 模式,可以和 Consul 結合,實現服務發現。內置的負載均衡,則需要自己手動維護后端節點。
Konga
Kong 官方並沒有為開源版本提供圖形界面,只在其企業版提供了此功能。但是我們有一些開源的圖形界面可以選擇,其中最知名的就是 Konga。
Konga 提供了多種功能,包括 service、router、Load Balance 的管理,並支持多用戶系統,方便給不同用戶划分不同的權限。
Kong 架構演進
Kong 經歷了幾次架構演進,終於演進到了目前比較流行的微服務架構方式。整體來說該架構主要經歷了如下演進:DB Mode(0.x-1.0)→ DB-less Mode(1.1-1.5)→ Hybird Mode(2.0+)
下面我們分別介紹以下幾種模式,以及它們的適用場景。
DB Mode:最早的 Kong 版本就是使用這種方式,每個 Kong 都連接了后端 DB,這個 DB 可選為 PostgreSQL 或者 Cassandra。但是這個模式之前有個很嚴重的 Bug,就是在流量大的時候更新路由配置,會導致 Kong 產生劇烈的抖動。
DB-less Mode:無數據庫模式,也就是通過聲明式的配置方式來運行 Kong。這種方式從某種意義來說是一種倒退,因為它讓 Kong 無法通過 API 控制,也就是回到了原始的 Nginx 配置模式,相當於 Service Mesh 失去了控制面。但也因為回歸了原始的方式,不會出現 DB Mode 更新配置時導致的一些抖動問題。
Hybird Mode:其實也就是引入了 Service Mesh 中的控制面和數據面的模式。相對於上面兩種模式有巨大的架構優勢,Hybird Mode 通過控制面收斂了數據庫的操作行為,Kong 的數據節點不需要再直接連接到 DB 層。這種模式有以下兩種優勢。僅控制面節點需要連接到 DB,而不是所有數據節點連接到 DB,對 DB 的壓力大大減少,也就降低了數據面出現抖動的概率。易於管理,不用像之前的 API 調用 Kong 的數據節點時再操作 Admin API 變更,只需與控制面交互即可。
Zuul
Zuul是Netflix開源的微服務網關,它可以和Eureka、Ribbon、Hystrix等組件配合使用,Zuul組件的核心是一系列的過濾器,這些過濾器可以完成以下功能:
動態路由:動態將請求路由到不同后端集群
壓力測試:逐漸增加指向集群的流量,以了解性能
負載分配:為每一種負載類型分配對應容量,並棄用超出限定值的請求
靜態響應處理:邊緣位置進行響應,避免轉發到內部集群身份認證和安全: 識別每一個資源的驗證要求,並拒絕那些不符的請求。Spring Cloud對Zuul進行了整合和增強。
動態路由
動態路由是動態的將客戶端的請求路由到后端不同的服務上,如果沒有網關去做統一的路由,那么客戶端就需要關注后端 N 個服務。
左邊的圖沒有使用網關,客戶端調用服務時就需要訪問服務各自的接口,如客戶端調用 A 服務的接口就需要請求 a.com,而對需要訪問服務的客戶端來說訪問流程越簡單越好,現在需要關注多個 API 提供方,無疑提高了訪問的復雜度。
右邊的圖使用了網關,使用網關后,客戶端只需要關注網關的地址,也就是 gateway.com。不再需要關注多個 API 提供方,由網關統一路由到后端的具體服務中,這其實跟我之前講的集中式負載均衡的概念類似,這樣的好處是對客戶端來說訪問服務的流程簡單了,關注的點少了。
另外一個好處就是可以在后端做 API 聚合操作,比如客戶端要展示一個商品詳情,里面有商品基本信息、庫存信息等,如果沒有聚合,就需要調用基本信息的接口,然后再調用庫存信息的接口,如果做了聚合,客戶端只需要調用一個接口,這個接口中包含了所有需要的信息,減少了前后端交互的次數,提升了用戶的體驗。
請求監控
請求監控可以對整個系統的請求進行監控,詳細地記錄請求響應日志,可以實時統計當前系統的訪問量及監控狀態。
如果沒有使用網關的話,記錄請求信息需要在各個服務中去做。當網關出現在我們的架構中后,所有客戶端的請求都會經過網關來做路由分發,入口統一了,很多事情也就好處理了,我們只需要在網關中統一進行請求信息的記錄,就可以基於這些記錄做實時的數據分析,比如並發調用量,根據數據分析決定是否要動態限流,分析是否有爬蟲請求等多維數據結果。給業務方提供正確實時的決策信息,是非常有價值的。
認證鑒權
認證鑒權可以對每一個訪問請求做認證,拒絕非法請求,保護后端的服務。微服務架構下,如果沒有使用網關,那么客戶端需要直接跟多個服務進行交互,當請求到達對應的服務時,就必須驗證當前的請求有沒有登錄,有沒有權限訪問。訪問 A 服務需要驗證一次,訪問 B 服務也需要驗證一次,每個服務都要做重復的工作。
當我們使用網關后,就可以在網關中做統一的驗證邏輯了,唯一要做的工作就是在網關驗證完成后,需要將用戶信息傳遞給后端服務,后端服務默認相信當前的請求已經在網關中通過驗證,它不會再去做驗證的邏輯,但是當前請求對應的用戶信息要告訴后端服務,可以將用戶信息通過 HTTP 請求頭傳遞給路由的后端服務。
壓力測試
壓力測試是一項很重要的工作,像一些電商公司需要模擬更多真實的用戶並發量來保證大促時系統的穩定,通過 Zuul 可以動態地將測試請求轉發到后端服務的集群中,還可以識別測試流量和真實流量,用來做一些特殊處理。
對於測試請求,可以在請求頭中添加標識,讓網關能夠識別這是一個測試請求,當識別到測試請求后,根據對應的規則進行路由,這里可以用配置中心存儲規則,測試請求路由到測試服務,測試服務會有單獨的測試數據庫,這樣測試的請求就不會影響到正式的服務和數據庫了。
灰度發布
灰度發布可以保障整體系統的穩定性,在初始灰度的時候就可以及時發現、調整問題,以降低影響范圍。
需要發布新版本的時候,不會立即將老的服務停止,去發布新的服務。而是先發布新版本的服務,比如之前的版本是 1.0,那么現在發布的版本就是 1.1,發布后,需要通過測試請求對 1.1 版本的服務進行測試,如果沒發現什么問題,就可以將正常的請求轉發過來了。如果測試中發現問題,可以直接停掉 1.1 版本的服務,就算不停掉也沒關系,不會影響到正常用戶的使用。
過濾器
過濾器是 Zuul 中最核心的內容,過濾器可以對請求或響應結果進行處理,Zuul 還支持動態加載、編譯、運行這些過濾器,過濾器的使用方式是采取責任鏈的方式進行處理,過濾器之間通過 RequestContext 來傳遞上下文,通過過濾器可以擴展很多高級功能。Zuul 中的過濾器總共有 4 種類型,每種類型都有對應的使用場景。
pre 過濾器:可以在請求被路由之前調用。適用於身份認證的場景,認證通過后再繼續執行下面的流程。
route 過濾器:在路由請求時被調用。適用於灰度發布的場景,在將要路由的時候可以做一些自定義的邏輯。
post 過濾器:在 route 和 error 過濾器之后被調用。這種過濾器將請求路由到達具體的服務之后執行。適用於添加響應頭,記錄響應日志等應用場景。
error 過濾器:處理請求發生錯誤時被調用。在執行過程中發送錯誤時會進入 error 過濾器,可以用來統一記錄錯誤信息。
請求生命周期
當一個請求進來時,會先進入 pre 過濾器,在 pre 過濾器執行完后,接着就到了 routing 過濾器中,開始路由到具體的服務中,路由完成后,接着就到了 post 過濾器中,然后將請求結果返回給客戶端。如果在這個過程中出現異常,則會進入 error 過濾器中,這就是請求在整個 Zuul 中的生命周期。
性能問題
Zuul1x 版本本質上就是一個同步Servlet,采用多線程阻塞模型進行請求轉發。簡單講,每來一個請求,Servlet容器要為該請求分配一個線程專門負責處理這個請求,直到響應返回客戶端這個線程才會被釋放返回容器線程池。如果后台服務調用比較耗時,那么這個線程就會被阻塞,阻塞期間線程資源被占用,不能干其它事情。我們知道Servlet容器線程池的大小是有限制的,當前端請求量大,而后台慢服務比較多時,很容易耗盡容器線程池內的線程,造成容器無法接受新的請求。
不支持任何長連接,如 websocket
Spring Cloud Gateway
Spring Cloud Gateway 是 Spring 官方自己開發的一款 API 網關。在具體展開介紹 Spring Cloud Gateway 之前,我們有必要對它和 Netflix Zuul 做一個對比。通過上一課時的分析,我們知道 Zuul 的實現原理是對 Servlet 的一層封裝,通信模式上采用的是阻塞式 I/O。而在技術體系上,Spring Cloud Gateway 基於最新Spring 5 和 Spring Boot 2,以及用於響應式編程的 Project Reactor 框架,提供的是響應式、非阻塞式 I/O 模型。所以較之 Netflix Zuul,性能上Spring Cloud Gateway 顯然要更勝一籌。
另一方面,從功能上,Spring Cloud Gateway 也比 Zuul 更為豐富。除了通用的服務路由機制之外,Spring Cloud Gateway 還支持請求限流等面向服務容錯方面的功能,同樣也能與 Hystrix 等框架進行良好的集成。
1 . 路由(route) 路由是網關最基礎的部分,路由信息由一個ID、一個目的URL、一組斷言工廠和一組Filter組成。如果斷言為真,則說明請求URL和配置的路由匹配。
2.斷言(predicates) Java8中的斷言函數,Spring Cloud Gateway中的斷言函數輸入類型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的斷言函數允許開發者去定義匹配來自Http Request中的任何信息,比如請求頭和參數等。
3.過濾器(filter) 一個標准的Spring webFilter,Spring Cloud Gateway中的Filter分為兩種類型,分別是Gateway Filter和Global Filter。過濾器Filter可以對請求和響應進行處理。
過濾器基礎
(1) 過濾器的生命周期
Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么豐富,它只有兩個:“pre” 和 “post”。
- PRE : 這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集群中選擇請求的微服務、記錄調試信息等。
- POST :這種過濾器在路由到微服務以后執行。這種過濾器可用來為響應添加標准的 HTTPHeader、收集統計信息和指標、將響應從微服務發送給客戶端等。
( 2) 過濾器類型
Spring Cloud Gateway 的 Filter 從作用范圍可分為另外兩種GatewayFilter 與 GlobalFilter。
- GatewayFilter :應用到單個路由或者一個分組的路由上。
- GlobalFilter :應用到所有的路由上。
統一鑒權
內置的過濾器已經可以完成大部分的功能,但是對於企業開發的一些業務功能處理,還是需要我們自己編寫過濾器來實現的,那么我們一起通過代碼的形式自定義一個過濾器,去完成統一的權限校驗。
開發中的鑒權邏輯:
當客戶端第一次請求服務時,服務端對用戶進行信息認證(登錄)認證通過,將用戶信息進行加密形成 token,返回給客戶端,作為登錄憑證以后每次請求,客戶端都攜帶認證的 token服務端對 token進行解密,判斷是否有效。
基於Filter的限流
SpringCloudGateway官方就提供了基於令牌桶的限流支持。基於其內置的過濾器工廠RequestRateLimiterGatewayFilterFactory 實現。在過濾器工廠中是通過Redis和lua腳本結合的方式進行流量控制。
原理:
Gateway的客戶端向 Spring Cloud Gateway 發送請求,請求首先被 HttpWebHandlerAdapter 進行提取組裝成網關上下文,然后網關的上下文會傳遞
到 DispatcherHandler 。 DispatcherHandler 是所有請求的分發處理器, DispatcherHandler 主要負責分發請求對應的處理器。比如請求分發到對應的 RoutePredicateHandlerMapping (路由斷言處理映射器)。路由斷言處理映射器主要作用用於路由查找,以及找到路由后返回對應的FilterWebHandler 。 FilterWebHandler 主要負責組裝Filter鏈並調用Filter執行一系列的Filter處理,然后再把請求轉到后端對應的代理服務處理,處理完畢之后將Response返回到Gateway客戶端。