億級流量架構之閘道器設計思路、常見閘道器對比

等不到的口琴發表於2021-02-25

本文準備圍繞七個點來講閘道器,分別是閘道器的基本概念、閘道器設計思路、閘道器設計重點、流量閘道器、業務閘道器、常見閘道器對比,對基礎概念熟悉的朋友可以根據目錄檢視自己感興趣的部分。

什麼是閘道器

閘道器,很多地方將閘道器比如成門, 沒什麼問題, 但是需要區分閘道器與網橋的區別,

網橋工作在資料鏈路層,在不同或相同型別的LAN之間儲存並轉發資料幀,必要時進行鏈路層上的協議轉換。可連線兩個或多個網路,在其中傳送資訊包。

閘道器是一個大概念,不具體特指一類產品,只要連線兩個不同的網路都可以叫閘道器,網橋一般只轉發資訊,而閘道器可能進行包裝。

閘道器通俗理解

根據閘道器的特性,舉個例子:

假如你要去找集團老闆(這兒只是舉個例子), 大家都知道老闆肯定不是誰想見就能見的, 也怕壞人嘛, 那麼你去老闆所在的辦公樓,假如是集團總部, 大樓這個門就充當了閘道器的角色, 大門一般都有看門員 ,看門員會做哪些事情呢?

首先所有想見老闆的人肯定都得從這個門進(統一入口), 這個門相當於將辦公室和外界隔離了,主要為了保護裡面的安全以及正常工作, 來到這個門之後, 門衛肯定會讓你出示相關證件(鑑權檢驗), 意思就是判斷你要見老闆這個請求是否合理, 如果不合理直接就拒絕了, 讓你回家等訊息 , 如果鑑權之後, 發現你找老闆其實只是為了和他談談兩元店的生意, 門衛會跟你說這個用不著找老闆, 你去集團投資部就行了(動態路由, 將請求路由到不同的後端叢集中), 此時會對你進行一些包裝,例如給你出具一個訪問證類似的,然後告訴你路該怎麼走,等等。

你看看,閘道器的作用是不是就是這三個, 最終目的就是減少你與集團的耦合,具體到計算機上就是減少客戶端與服務端的耦合,如果沒有閘道器意味著所有請求都會直接呼叫伺服器上的資源,這樣耦合太強了,伺服器出了問題,客戶端會直接報錯, 例如老闆換工作的地方了,如果沒有閘道器你直接去原來的地方找, 肯定會被告知老闆不在這兒。

為什麼需要閘道器

當使用單體應用程式架構時,客戶端(Web 或移動端)通過向後端應用程式發起一次 REST 呼叫來獲取資料。負載均衡器將請求路由給 N 個相同的應用程式例項中的一個。然後應用程式會查詢各種資料庫表,並將響應返回給客戶端。微服務架構下,單體應用被切割成多個微服務,如果將所有的微服務直接對外暴露,勢必會出現安全方面的各種問題,另外內外耦合嚴重。

客戶端可以直接向每個微服務傳送請求,其問題主要如下:

  • 客戶端需求和每個微服務暴露的細粒度 API 不匹配。
  • 部分服務使用的協議不是Web友好協議。可能使用 Thrift 二進位制 RPC,也可能使用 AMQP 訊息傳遞協議。
  • 微服務難以重構。如果合併兩個服務,或者將一個服務拆分成兩個或更多服務,這類重構就非常困難了。

服務端的各個服務直接暴露給客戶端呼叫勢必會引起各種問題。同時,服務端的各個服務可擴充套件和伸縮性很差。API 閘道器是微服務架構中的基礎元件,位於接入層之下和業務服務層之上,如前所述的這些功能適合在 API 閘道器實現。

閘道器與伺服器叢集

回到我們伺服器上,下面圖介紹了閘道器(Gateway)作用,可知 Gateway 方式下的架構,可以細到為每一個服務的例項配置一個自己的 Gateway,也可以粗到為一組服務配置一個,甚至可以粗到為整個架構配置一個接入的 Gateway。於是,整個系統架構的複雜度就會變得簡單可控起來。

這張圖展示了一個多層 Gateway 架構,其中有一個總的 Gateway 接入所有的流量(流量閘道器),並分發給不同的子系統,還有第二級 Gateway 用於做各個子系統的接入 Gateway(業務閘道器)。可以看到,閘道器所管理的服務粒度可粗可細。通過閘道器,我們可以把分散式架構組織成一個星型架構,由網路對服務的請求進行路由和分發。下面來聊聊好的閘道器應該具備哪些功能,也就是閘道器設計模式。

閘道器設計思路

一個閘道器需要有以下的功能:

請求路由

閘道器一定要有請求路由的功能。這樣一來,對於呼叫端來說,也是一件非常方便的事情。因為呼叫端不需要知道自己需要用到的其它服務的地址,全部統一地交給 Gateway 來處理。

服務註冊

為了能夠代理後面的服務,並把請求路由到正確的位置上,閘道器應該有服務註冊功能,也就是後端的服務例項可以把其提供服務的地址註冊、取消註冊。一般來說,註冊也就是註冊一些 API 介面。比如,HTTP 的 Restful 請求,可以註冊相應 API 的 URI、方法、HTTP 頭。 這樣,Gateway 就可以根據接收到的請求中的資訊來決定路由到哪一個後端的服務上。

負載均衡

因為一個閘道器可以接收多個服務例項,所以閘道器還需要在各個對等的服務例項上做負載均衡策略。簡單點就是直接 Round-Robin 輪詢,複雜點的可以設定上權重進行分發,再複雜一點還可以做到 session 粘連。

彈力設計

閘道器還可以把彈力設計中的那些非同步、重試、冪等、流控、熔斷、監視等都可以實現進去。這樣,同樣可以像 Service Mesh 那樣,讓應用服務只關心自己的業務邏輯(或是說資料面上的事)而不是控制邏輯(控制面)。

安全方面

SSL 加密及證照管理、Session 驗證、授權、資料校驗,以及對請求源進行惡意攻擊的防範。錯誤處理越靠前的位置就是越好,所以,閘道器可以做到一個全站的接入元件來對後端的服務進行保護。當然,閘道器還可以做更多更有趣的事情,比如:灰度釋出、API聚合、API編排。

灰度釋出

閘道器完全可以做到對相同服務不同版本的例項進行導流,還可以收集相關的資料。這樣對於軟體質量的提升,甚至產品試錯都有非常積極的意義。

API 聚合

使用閘道器可以將多個單獨請求聚合成一個請求。在微服務體系的架構中,因為服務變小了,所以一個明顯的問題是,客戶端可能需要多次請求才能得到所有的資料。這樣一來,客戶端與後端之間的頻繁通訊會對應用程式的效能和規模產生非常不利的影響。於是,我們可以讓閘道器來幫客戶端請求多個後端的服務(有些場景下完全可以併發請求),然後把後端服務的響應結果拼裝起來,回傳給客戶端(當然,這個過程也可以做成非同步的,但這需要客戶端的配合)。

API 編排

同樣在微服務的架構下,要走完一個完整的業務流程,我們需要呼叫一系列 API,就像一種工作流一樣,這個事完全可以通過網頁來編排這個業務流程。我們可能通過一個 DSL 來定義和編排不同的 API,也可以通過像 AWS Lambda 服務那樣的方式來串聯不同的 API。

閘道器設計重點

閘道器設計重點主要是三個, 高效能、高可用、高擴充套件:

高效能

在技術設計上,閘道器不應該也不能成為效能的瓶頸。對於高效能,最好使用高效能的程式語言來實現,如 C、C++、Go 和 Java。閘道器對後端的請求,以及對前端的請求的服務一定要使用非同步非阻塞的 I/O 來確保後端延遲不會導致應用程式中出現效能問題。C 和 C++ 可以參看 Linux 下的 epoll 和 Windows 的 I/O Completion Port 的非同步 IO 模型,Java 下如 Netty、Spring Reactor 的 NIO 框架。

高可用

因為所有的流量或呼叫經過閘道器,所以閘道器必須成為一個高可用的技術元件,它的穩定直接關係到了所有服務的穩定。閘道器如果沒有設計,就會成變一個單點故障。因此,一個好的閘道器至少要做到以下幾點。

  • 叢集化。閘道器要成為一個叢集,其最好可以自己組成一個叢集,並可以自己同步叢集資料,而不需要依賴於一個第三方系統來同步資料。
  • 服務化。閘道器還需要做到在不間斷的情況下修改配置,一種是像 Nginx reload 配置那樣,可以做到不停服務,另一種是最好做到服務化。也就是說,得要有自己的 Admin API 來在執行時修改自己的配置。
  • 持續化。比如重啟,就是像 Nginx 那樣優雅地重啟。有一個主管請求分發的主程式。當我們需要重啟時,新的請求被分配到新的程式中,而老的程式處理完正在處理的請求後就退出。

高擴充套件

因為閘道器需要承接所有的業務流量和請求,所以一定會有或多或少的業務邏輯。而我們都知道,業務邏輯是多變和不確定的。比如,需要在閘道器上加入一些和業務相關的東西。因此,一個好的 Gateway 還需要是可以擴充套件的,並能進行二次開發的。當然,像 Nginx 那樣通過 Module 進行二次開發的固然可以。

另外,在運維方面,閘道器應該有以下幾個設計原則。

  • 業務鬆耦合,協議緊耦合。在業務設計上,閘道器不應與後面的服務之間形成服務耦合,也不應該有業務邏輯。閘道器應該是在網路應用層上的元件,不應該處理通訊協議體,只應該解析和處理通訊協議頭。另外,除了服務發現外,閘道器不應該有第三方服務的依賴。
  • 應用監視,提供分析資料。閘道器上需要考慮應用效能的監控,除了有相應後端服務的高可用的統計之外,還需要使用 Tracing ID 實施分散式鏈路跟蹤,並統計好一定時間內每個 API 的吞吐量、響應時間和返回碼,以便啟動彈力設計中的相應策略。
  • 用彈力設計保護後端服務。閘道器上一定要實現熔斷、限流、重試和超時等彈力設計。如果一個或多個服務呼叫花費的時間過長,那麼可接受超時並返回一部分資料,或是返回一個閘道器裡的快取的上一次成功請求的資料。你可以考慮一下這樣的設計。
  • DevOps。因為閘道器這個元件太關鍵了,所以需要 DevOps 這樣的東西,將其發生故障的概率降到最低。這個軟體需要經過精良的測試,包括功能和效能的測試,還有浸泡測試。還需要有一系列自動化運維的管控工具。

閘道器設計注意事項

  1. 不要在閘道器中的程式碼裡內建聚合後端服務的功能,而應考慮將聚合服務放在閘道器核心程式碼之外。可以使用 Plugin 的方式,也可以放在閘道器後面形成一個 Serverless 服務。
  2. 閘道器應該靠近後端服務,並和後端服務使用同一個內網,這樣可以保證閘道器和後端服務呼叫的低延遲,並可以減少很多網路上的問題。這裡多說一句,閘道器處理的靜態內容應該靠近使用者(應該放到 CDN 上),而閘道器和此時的動態服務應該靠近後端服務。
  3. 閘道器也需要做容量擴充套件,所以需要成為一個叢集來分擔前端帶來的流量。這一點,要麼通過 DNS 輪詢的方式實現,要麼通過 CDN 來做流量排程,或者通過更為底層的效能更高的負載均衡裝置。
  4. 對於服務發現,可以做一個時間不長的快取,這樣不需要每次請求都去查一下相關的服務所在的地方。當然,如果你的系統不復雜,可以考慮把服務發現的功能直接整合進閘道器中。
  5. 為閘道器考慮 bulkhead 設計方式。用不同的閘道器服務不同的後端服務,或是用不同的閘道器服務前端不同的客戶。

另外,因為閘道器是為使用者請求和後端服務的橋接裝置,所以需要考慮一些安全方面的事宜。具體如下:

  1. 加密資料。可以把 SSL 相關的證照放到閘道器上,由閘道器做統一的 SSL 傳輸管理。
  2. 校驗使用者的請求。一些基本的使用者驗證可以放在閘道器上來做,比如使用者是否已登入,使用者請求中的 token 是否合法等。但是,我們需要權衡一下,閘道器是否需要校驗使用者的輸入。因為這樣一來,閘道器就需要從只關心協議頭,到需要關心協議體。而協議體中的東西一方面不像協議頭是標準的,另一方面解析協議體還要耗費大量的執行時間,從而降低閘道器的效能。對此,我想說的是,看具體需求,一方面如果協議體是標準的,那麼可以幹;另一方面,對於解析協議所帶來的效能問題,需要做相應的隔離。
  3. 檢測異常訪問。閘道器需要檢測一些異常訪問,比如,在一段比較短的時間內請求次數超過一定數值;還比如,同一客戶端的 4xx 請求出錯率太高……對於這樣的一些請求訪問,閘道器一方面要把這樣的請求遮蔽掉,另一方面需要發出警告,有可能會是一些比較重大的安全問題,如被黑客攻擊。

流量閘道器

流量閘道器,顧名思義就是控制流量進入叢集的閘道器,有很多工作需要在這一步做,對於一個服務叢集,勢必有很多非法的請求或者無效的請求,這時候要將請求拒之門外,降低叢集的流量壓力。

定義全域性性的、跟具體的後端業務應用和服務完全無關的策略閘道器就是上圖所示的架構模型——流量閘道器。流量閘道器通常只專注於全域性的Api管理策略,比如全域性流量監控、日誌記錄、全侷限流、黑白名單控制、接入請求到業務系統的負載均衡等,有點類似防火牆。Kong 就是典型的流量閘道器。

下面是kong的架構圖,來自官網:

這裡需要補充一點的是,業務閘道器一般部署在流量閘道器之後、業務系統之前,比流量閘道器更靠近業務系統。通常API網指的是業務閘道器。 有時候我們也會模糊流量閘道器和業務閘道器,讓一個閘道器承擔所有的工作,所以這兩者之間並沒有嚴格的界線。

業務閘道器

當一個單體應用被拆分成許許多多的微服務應用後,也帶來了一些問題。一些與業務非強相關的功能,比如許可權控制、日誌輸出、資料加密、熔斷限流等,每個微服務應用都需要,因此存在著大量重複的程式碼實現。而且由於系統的迭代、人員的更替,各個微服務中這些功能的實現細節出現了較大的差異,導致維護成本變高。另一方面,原先單體應用下非常容易做的介面管理,在服務拆分後沒有了一個集中管理的地方,無法統計已存在哪些介面、介面定義是什麼、執行狀態如何。

閘道器就是為了解決上述問題。作為微服務體系中的核心基礎設施,一般需要具備介面管理、協議適配、熔斷限流、安全防護等功能,各種開源的閘道器產品(比如 zuul)都提供了優秀高可擴充套件性的架構、可以很方便的實現我們需要的一些功能、比如鑑權、日誌監控、熔斷限流等。

與流量閘道器相對應的就是業務閘道器,業務閘道器更靠近我們的業務,也就是與伺服器應用層打交道,那麼有很多應用層需要考慮的事情就可以依託業務閘道器,例如線上程模型、協議適配、熔斷限流,服務編排等。下面看看業務閘道器體系結構:

圖片來自:業務閘道器的落地實踐

從這個途中可以看出業務閘道器主要職責以及所做的事情, 目前業務閘道器比較成熟的 API 閘道器框架產品有三個 分別是:Zuul1、Zuul2 和 SpringCloud Gateway, 後面再進行對比。

常見閘道器對比

既然對比,就先巨集觀上對各種閘道器有一個瞭解,後面再挑一些常用的或者說應用廣泛的詳細瞭解。

目前常見的開源閘道器大致上按照語言分類有如下幾類:

  • Nginx+lua:OpenResty、Kong、Orange、Abtesting gateway 等
  • Java:Zuul/Zuul2、Spring Cloud Gateway、Kaazing KWG、gravitee、Dromara soul 等
  • Go:Janus、fagongzi、Grpc-gateway
  • Dotnet:Ocelot
  • NodeJS:Express Gateway、Micro Gateway

按照使用數量、成熟度等來劃分,主流的有 4 個:

  • OpenResty
  • Kong
  • Zuul/Zuul2
  • Spring Cloud Gateway

OpenResty

相關連線: 官網B站Github

OpenResty是一個流量閘道器,根據前面對流量閘道器的介紹就可以知道流量閘道器的指責。

OpenResty基於 Nginx 與 Lua 的高效能 Web 平臺,其內部整合了大量精良的 Lua 庫、第三方模組以及大多數的依賴項。用於方便地搭建能夠處理超高併發、擴充套件性極高的動態 Web 應用、Web 服務和動態閘道器。

通過揉和眾多設計良好的 Nginx 模組,OpenResty 有效地把 Nginx 伺服器轉變為一個強大的 Web 應用伺服器,基於它開發人員可以使用 Lua 程式語言對 Nginx 核心以及現有的各種 Nginx C 模組進行指令碼程式設計,構建出可以處理一萬以上併發請求的極端高效能的 Web 應用

OpenResty 最早是順應 OpenAPI 的潮流做的,所以 Open 取自“開放”之意,而Resty便是 REST 風格的意思。雖然後來也可以基於 ngx_openresty 實現任何形式的 web service 或者傳統的 web 應用。

也就是說 Nginx 不再是一個簡單的靜態網頁伺服器,也不再是一個簡單的反向代理了。第二代的 openresty 致力於通過一系列 nginx 模組,把nginx擴充套件為全功能的 web 應用伺服器。

ngx_openresty 是使用者驅動的專案,後來也有不少國內使用者的參與,從 openresty.org 的點選量分佈上看,國內和國外的點選量基本持平。

ngx_openresty 目前有兩大應用目標:

  1. 通用目的的 web 應用伺服器。在這個目標下,現有的 web 應用技術都可以算是和 OpenResty 或多或少有些類似,比如 Nodejs, PHP 等等。ngx_openresty 的效能(包括記憶體使用和 CPU 效率)算是最大的賣點之一。
  2. Nginx 的指令碼擴充套件程式設計,用於構建靈活的 Web 應用閘道器和 Web 應用防火牆。有些類似的是 NetScaler。其優勢在於 Lua 程式設計帶來的巨大靈活性。

Kong

相關連線: 官網Github

Kong基於OpenResty開發,也是流量層閘道器, 是一個雲原生、快速、可擴充套件、分散式的Api 閘道器。繼承了OpenResty的高效能、易擴充套件性等特點。Kong通過簡單的增加機器節點,可以很容易的水平擴充套件。同時功能外掛化,可通過外掛來擴充套件其能力。而且在任何基礎架構上都可以執行。具有以下特性:

  • 提供了多樣化的認證層來保護Api。
  • 可對出入流量進行管制。
  • 提供了視覺化的流量檢查、監視分析Api。
  • 能夠及時的轉換請求和相應。
  • 提供log解決方案
  • 可通過api呼叫Serverless 函式。

Kong解決了什麼問題

當我們決定對應用進行微服務改造時,應用客戶端如何與微服務互動的問題也隨之而來,畢竟服務數量的增加會直接導致部署授權、負載均衡、通訊管理、分析和改變的難度增加。

面對以上問題,API GATEWAY是一個不錯的解決方案,其所提供的訪問限制、安全、流量控制、分析監控、日誌、請求轉發、合成和協議轉換功能,可以解放開發者去把精力集中在具體邏輯的程式碼,而不是把時間花費在考慮如何解決應用和其他微服務連結的問題上。

圖片來自Kong官網:

可以看到Kong解決的問題。專注於全域性的Api管理策略,全域性流量監控、日誌記錄、全侷限流、黑白名單控制、接入請求到業務系統的負載均衡等。

Kong的優點以及效能

在眾多 API GATEWAY 框架中,Mashape 開源的高效能高可用API閘道器和API服務管理層——KONG(基於 NGINX+Lua)特點尤為突出,它可以通過外掛擴充套件已有功能,這些外掛(使用 lua 編寫)在API請求響應迴圈的生命週期中被執行。於此同時,KONG本身提供包括 HTTP 基本認證、金鑰認證、CORS、TCP、UDP、檔案日誌、API請求限流、請求轉發及 NGINX 監控等基本功能。目前,Kong 在 Mashape 管理了超過 15,000 個 API,為 200,000 開發者提供了每月數十億的請求支援。

Kong架構

Kong提供一些列的服務,這就不得不談談內部的架構:

首先最底層是基於Nginx, Nginx是高效能的基礎層, 一個良好的負載均衡、反向代理器,然後在此基礎上增加Lua指令碼庫,形成了OpenResty,攔截請求, 響應生命週期,可以通過Lua編寫指令碼,所以外掛比較豐富。

關於Kong的一些外掛庫以及如何配置,可以參考簡書:開源API閘道器係統(Kong教程)入門到精通

Zuul1.0

Zuul是所有從裝置和web站點到Netflix流媒體應用程式後端請求的前門。作為一個邊緣服務應用程式,Zuul被構建來支援動態路由、監視、彈性和安全性。它還可以根據需要將請求路由到多個Amazon自動伸縮組。

Zuul使用了一系列不同型別的過濾器,使我們能夠快速靈活地將功能應用到服務中。

過濾器

過濾器是Zuul的核心功能。它們負責應用程式的業務邏輯,可以執行各種任務。

  • Type : 通常定義過濾器應用在哪個階段
  • Async : 定義過濾器是同步還是非同步
  • Execution Order : 執行順序
  • Criteria : 過濾器執行的條件
  • Action : 如果條件滿足,過濾器執行的動作

Zuul提供了一個動態讀取、編譯和執行這些過濾器的框架。過濾器之間不直接通訊,而是通過每個請求特有的RequestContext共享狀態。

下面是Zuul的一些過濾器:

Incoming

Incoming過濾器在請求被代理到Origin之前執行。這通常是執行大部分業務邏輯的地方。例如:認證、動態路由、速率限制、DDoS保護、指標。

Endpoint

Endpoint過濾器負責基於incoming過濾器的執行來處理請求。Zuul有一個內建的過濾器(ProxyEndpoint),用於將請求代理到後端伺服器,因此這些過濾器的典型用途是用於靜態端點。例如:健康檢查響應,靜態錯誤響應,404響應。

Outgoing

Outgoing過濾器在從後端接收到響應以後執行處理操作。通常情況下,它們更多地用於形成響應和新增指標,而不是用於任何繁重的工作。例如:儲存統計資訊、新增/剝離標準標題、向實時流傳送事件、gziping響應。

過濾器型別

下面是與一個請求典型的生命週期對應的標準的過濾器型別:

  • PRE : 路由到Origin之前執行
  • ROUTING : 路由到Origin期間執行
  • POST : 請求被路由到Origin之後執行
  • ERROR : 發生錯誤的時候執行

這些過濾器幫助我們執行以下功能:

  • 身份驗證和安全性 : 識別每個資源的身份驗證需求,並拒絕不滿足它們的請求
  • 監控 : 在邊緣跟蹤有意義的資料和統計資料,以便給我們一個準確的生產檢視
  • 動態路由 : 動態路由請求到不同的後端叢集
  • 壓力測試 : 逐漸增加叢集的流量,以評估效能
  • 限流 : 為每種請求型別分配容量,並丟棄超過限制的請求
  • 靜態響應處理 : 直接在邊緣構建一些響應,而不是將它們轉發到內部叢集

Zuul 1.0 請求生命週期

Netflix宣佈了通用API閘道器Zuul的架構轉型。Zuul原本採用同步阻塞架構,轉型後叫作Zuul2,採用非同步非阻塞架構。Zuul2和Zuul1在架構方面的主要區別在於,Zuul2執行在非同步非阻塞的框架上,比如Netty。Zuul1依賴多執行緒來支援吞吐量的增長,而Zuul 2使用的Netty框架依賴事件迴圈和回撥函式。

Zuul2.0

Zuul 2.0 架構圖

上圖是Zuul2的架構,和Zuul1沒有本質區別,兩點變化:

  1. 前端用Netty Server代替Servlet,目的是支援前端非同步。後端用Netty Client代替Http Client,目的是支援後端非同步。
  2. 過濾器換了一下名字,用Inbound Filters代替Pre-routing Filters,用Endpoint Filter代替Routing Filter,用Outbound Filters代替Post-routing Filters。

Inbound Filters : 路由到 Origin 之前執行,可以用於身份驗證、路由和裝飾請求

Endpoint Filters : 可用於返回靜態響應,否則內建的ProxyEndpoint過濾器將請求路由到Origin

Outbound Filters : 從Origin那裡獲取響應後執行,可以用於度量、裝飾使用者的響應或新增自定義header

有兩種型別的過濾器:sync 和 async。因為Zuul是執行在一個事件迴圈之上的,因此從來不要在過濾中阻塞。如果你非要阻塞,可以在一個非同步過濾器中這樣做,並且在一個單獨的執行緒池上執行,否則可以使用同步過濾器。

上文提到過Zuul2開始採用了非同步模型

優勢是非同步非阻塞模式啟動的執行緒很少,基本上一個CPU core上只需啟一個事件環處理執行緒,它使用的執行緒資源就很少,上下文切換(Context Switch)開銷也少。非阻塞模式可以接受的連線數大大增加,可以簡單理解為請求來了只需要進佇列,這個佇列的容量可以設得很大,只要不超時,佇列中的請求都會被依次處理。

不足,非同步模式讓程式設計模型變得複雜。一方面Zuul2本身的程式碼要比Zuul1複雜很多,Zuul1的程式碼比較容易看懂,Zuul2的程式碼看起來就比較費勁。另一方面非同步模型沒有一個明確清晰的請求->處理->響應執行流程(call flow),它的流程是通過事件觸發的,請求處理的流程隨時可能被切換斷開,內部實現要通過一些關聯id機制才能把整個執行流再串聯起來,這就給開發除錯運維引入了很多複雜性,比如你在IDE裡頭除錯非同步請求流就非常困難。另外ThreadLocal機制在這種非同步模式下就不能簡單工作,因為只有一個事件環執行緒,不是每個請求一個執行緒,也就沒有執行緒區域性的概念,所以對於CAT這種依賴於ThreadLocal才能工作的監控工具,呼叫鏈埋點就不好搞(實際可以工作但需要進行特殊處理)。

總體上,非同步非阻塞模式比較適用於IO密集型(IO bound)場景,這種場景下系統大部分時間在處理IO,CPU計算比較輕,少量事件環執行緒就能處理。

Zuul 與 Zuul 2 效能對比

圖片來源:Zuul's Journey to Non-Blocking

Netflix給出了一個比較模糊的資料,大致Zuul2的效能比Zuul1好20%左右,這裡的效能主要指每節點每秒處理的請求數。為什麼說模糊呢?因為這個資料受實際測試環境,流量場景模式等眾多因素影響,你很難復現這個測試資料。即便這個20%的效能提升是確實的,其實這個效能提升也並不大,和非同步引入的複雜性相比,這20%的提升是否值得是個問題。Netflix本身在其博文\(^2\)和ppt\(^1\)中也是有點含糊其詞,甚至自身都有一些疑問的。

Spring Cloud Gateway

相關連結:官網中文官方文件

SpringCloud Gateway 是 Spring Cloud 的一個全新專案,該專案是基於 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的閘道器,它旨在為微服務架構提供一種簡單有效的統一的 API 路由管理方式。

SpringCloud Gateway 作為 Spring Cloud 生態系統中的閘道器,目標是替代 Zuul,在Spring Cloud 2.0以上版本中,沒有對新版本的Zuul 2.0以上最新高效能版本進行整合,仍然還是使用的Zuul 2.0之前的非Reactor模式的老版本。而為了提升閘道器的效能,SpringCloud Gateway是基於WebFlux框架實現的,而WebFlux框架底層則使用了高效能的Reactor模式通訊框架Netty。

Spring Cloud Gateway 的目標,不僅提供統一的路由方式,並且基於 Filter 鏈的方式提供了閘道器基本的功能,例如:安全,監控/指標,和限流。

Spring Cloud Gateway 底層使用了高效能的通訊框架Netty

SpringCloud Gateway 特徵

SpringCloud官方,對SpringCloud Gateway 特徵介紹如下:

(1)基於 Spring Framework 5,Project Reactor 和 Spring Boot 2.0

(2)整合 Hystrix 斷路器

(3)整合 Spring Cloud DiscoveryClient

(4)Predicates 和 Filters 作用於特定路由,易於編寫的 Predicates 和 Filters

(5)具備一些閘道器的高階功能:動態路由、限流、路徑重寫

從以上的特徵來說,和Zuul的特徵差別不大。SpringCloud Gateway和Zuul主要的區別,還是在底層的通訊框架上。

簡單說明一下上文中的三個術語:

Filter(過濾器)

和Zuul的過濾器在概念上類似,可以使用它攔截和修改請求,並且對上游的響應,進行二次處理。過濾器為org.springframework.cloud.gateway.filter.GatewayFilter類的例項。

Route(路由)

閘道器配置的基本組成模組,和Zuul的路由配置模組類似。一個Route模組由一個 ID,一個目標 URI,一組斷言和一組過濾器定義。如果斷言為真,則路由匹配,目標URI會被訪問。

Predicate(斷言):

這是一個 Java 8 的 Predicate,可以使用它來匹配來自 HTTP 請求的任何內容,例如 headers 或引數。斷言的輸入型別是一個 ServerWebExchange。

幾種閘道器的對比

閘道器 限流 鑑權 監控 易用性 可維護性 成熟度
Spring Cloud Gateway 可以通過IP,使用者,叢集限流,提供了相應的介面進行擴充套件 普通鑑權、auth2.0 Gateway Metrics Filter 簡單易用 spring系列可擴充套件強,易配置 可維護性好 spring社群成熟,但gateway資源較少
Zuul2 可以通過配置檔案配置叢集限流和單伺服器限流亦可通過filter實現限流擴充套件 filter中實現 filter中實現 參考資料較少 可維護性較差 開源不久,資料少
OpenResty 需要lua開發 需要lua開發 需要開發 簡單易用,但是需要進行的lua開發很多 可維護性較差,將來需要維護大量lua指令碼 很成熟資料很多
Kong 根據秒,分,時,天,月,年,根據使用者進行限流。可在原碼的基礎上進行開發 普通鑑權,Key Auth鑑權,HMAC,auth2.0 可上報datadog,記錄請求數量,請求資料量,應答資料量,接收於傳送的時間間隔,狀態碼數量,kong內執行時間 簡單易用,api轉發通過管理員介面配置,開發需要lua指令碼 "可維護性較差,將來需要維護大量lua庫 相對成熟,使用者問題彙總,社群,外掛開源

站在巨人的肩膀上

1、Zuul's Journey to Non-Blocking

2、Zuul 2 : The Netflix Journey to Asynchronous, Non-Blocking Systems

相關文章