API 網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

前幾天拜讀了 OpsGenie 公司(一家致力於 Dev & Ops 的公司)的資深工程師 Turgay Çelik 博士寫的一篇文章(連接在文末),文中介紹了他們最初也是採用 Nginx 做爲單體應用的網關,後來接觸到微服務架構後開始逐漸採用了其餘組件。nginx

我對於所作的工做或者感興趣的技術,喜歡刨根問底,因此當讀一篇文章時發現沒有看到我想要看到的設計思想,我就會四處蒐集資料,此外這篇文章涉及了我正在搗鼓的 Spring Cloud,因此我就決定寫一篇文章,爭取能從設計思路上解釋爲何會有這樣的性能差別。程序員

技術介紹

文中針對 Nginx、ZUUL、Spring Cloud、Linkerd 等技術進行了對比(其實還有 Envoy UnderTow 也是屬於可選的 API 網關,本文不予涉及),那我就分別進行介紹,固然,首先得介紹 API 網關。spring

API 網關

API 網關出現的緣由是微服務架構的出現,不一樣的微服務通常會有不一樣的網絡地址,而外部客戶端可能須要調用多個服務的接口才能完成一個業務需求,若是讓客戶端直接與各個微服務通訊,會有如下的問題:後端

  1. 客戶端會屢次請求不一樣的微服務,增長了客戶端的複雜性。
  2. 存在跨域請求,在必定場景下處理相對複雜。
  3. 認證複雜,每一個服務都須要獨立認證。
  4. 難以重構,隨着項目的迭代,可能須要從新劃分微服務。例如,可能將多個服務合併成一個或者將一個服務拆分紅多個。若是客戶端直接與微服務通訊,那麼重構將會很難實施。
  5. 某些微服務可能使用了防火牆 / 瀏覽器不友好的協議,直接訪問會有必定的困難。

以上這些問題能夠藉助 API 網關解決。API 網關是介於客戶端和服務器端之間的中間層,全部的外部請求都會先通過 API 網關這一層。也就是說,API 的實現方面更多的考慮業務邏輯,而安全、性能、監控能夠交由 API 網關來作,這樣既提升業務靈活性又不缺安全性,典型的架構圖如圖所示:設計模式

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

使用 API 網關後的優勢以下:api

  • 易於監控。能夠在網關收集監控數據並將其推送到外部系統進行分析。
  • 易於認證。能夠在網關上進行認證,而後再將請求轉發到後端的微服務,而無須在每一個微服務中進行認證。
  • 減小了客戶端與各個微服務之間的交互次數。

NGINX 服務

Nginx 由內核和模塊組成,內核的設計很是微小和簡潔,完成的工做也很是簡單,僅僅經過查找配置文件與客戶端請求進行 URL 匹配,用於啓動不一樣的模塊去完成相應的工做。跨域

下面這張圖反應的是 HTTP 請求的常規處理流程:瀏覽器

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

Nginx 的模塊直接被編譯進 Nginx,所以屬於靜態編譯方式。啓動 Nginx 後,Nginx 的模塊被自動加載,不像 Apache,首先將模塊編譯爲一個 so 文件,而後在配置文件中指定是否進行加載。在解析配置文件時,Nginx 的每一個模塊都有可能去處理某個請求,可是同一個處理請求只能由一個模塊來完成。緩存

Nginx 在啓動後,會有一個 Master 進程和多個 Worker 進程,Master 進程和 Worker 進程之間是經過進程間通訊進行交互的,如圖所示。Worker 工做進程的阻塞點是在像 select()、epoll_wait() 等這樣的 I/O 多路複用函數調用處,以等待發生數據可讀 / 寫事件。Nginx 採用了異步非阻塞的方式來處理請求,也就是說,Nginx 是能夠同時處理成千上萬個請求的。一個 Worker 進程能夠同時處理的請求數只受限於內存大小,並且在架構設計上,不一樣的 Worker 進程之間處理併發請求時幾乎沒有同步鎖的限制,Worker 進程一般不會進入睡眠狀態,所以,當 Nginx 上的進程數與 CPU 核心數相等時(最好每個 Worker 進程都綁定特定的 CPU 核心),進程間切換的代價是最小的。安全

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

Netflix 的 Zuul

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

Zuul 是 Netflix 開源的微服務網關組件,它能夠和 Eureka、Ribbon、Hystrix 等組件配合使用。Zuul 的核心是一系列的過濾器,這些過濾器能夠完成如下功能:

  • 身份認證與安全:識別每一個資源的驗證要求,並拒絕那些與要求不符的請求。
  • 審查與監控:與邊緣位置追蹤有意義的數據和統計結果,從而帶來精確的生產視圖。
  • 動態路由:動態地將請求路由到不一樣的後端集羣。
  • 壓力測試:逐漸增長指向集羣的流量,以瞭解性能。
  • 負載分配:爲每一種負載類型分配對應容量,並棄用超出限定值的請求。
  • 靜態響應處理:在邊緣位置直接創建部分響應,從而避免其轉發到內部集羣。
  • 多區域彈性:跨越 AWS Region 進行請求路由,旨在實現 ELB(Elastic Load Balancing,彈性負載均衡)使用的多樣化,以及讓系統的邊緣更貼近系統的使用者。

上面說起的這些特性是 Nigix 所沒有的,這是由於 Netflix 公司創造 Zuul 是爲了解決雲端的諸多問題(特別是幫助 AWS 解決跨 Region 狀況下的這些特性實現),而不只僅是作一個相似於 Nigix 的反向代理,固然,咱們能夠僅使用反向代理功能,這裏很少作描述。

Zuul1 是基於 Servlet 框架構建,如圖所示,採用的是阻塞和多線程方式,即一個線程處理一次鏈接請求,這種方式在內部延遲嚴重、設備故障較多狀況下會引發存活的鏈接增多和線程增長的狀況發生。

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

Zuul2 的巨大區別是它運行在異步和無阻塞框架上,每一個 CPU 核一個線程,處理全部的請求和響應,請求和響應的生命週期是經過事件和回調來處理的,這種方式減小了線程數量,所以開銷較小。又因爲數據被存儲在同一個 CPU 裏,能夠複用 CPU 級別的緩存,前面說起的延遲和重試風暴問題也經過隊列存儲鏈接數和事件數方式減輕了不少(較線程切換來講輕量級不少,天然消耗較小)。這一變化必定會大大提高性能,咱們在後面的測試環節看看結果。

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

咱們今天談的是 API 網關性能,這一點也涉及到高可用,簡單介紹 Zuul 的高可用特性,高可用是很是關鍵的,由於外部請求到後端微服務的流量都會通過 Zuul,因此在生產環境中通常都須要部署高可用的 Zuul 來避免單點故障。通常咱們有兩種部署方案:

1. Zuul 客戶端註冊到 Eureka Server

這種狀況是比較簡單的狀況,只須要將多個 Zuul 節點註冊到 Eureka Server 上,就能夠實現 Zuul 的高可用。事實上,這種狀況下的高可用和其餘服務作高可用的方案沒有什麼區別。咱們來看下面這張圖,當 Zuul 客戶端註冊到 Eureka Server 上時,只須要部署多個 Zuul 節點就能夠實現高可用。Zuul 客戶端會自動從 Eureka Server 查詢 Zuul Server 列表,而後使用負載均衡組件(例如 Ribbon)請求 Zuul 集羣。

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

2.  Zuul 客戶端不能註冊到 Eureka Server

假如說咱們的客戶端是手機端 APP,那麼不可能經過方案 1 的方式註冊到 Eureka Server 上。這種狀況下,咱們能夠經過額外的負載均衡器來實現 Zuul 的高可用,例如 Nginx、HAProxy、F5 等。

如圖所示,Zuul 客戶端將請求發送到負載均衡器,負載均衡器將請求轉發到其代理的其中一個 Zuul 節點,這樣就能夠實現 Zuul 的高可用。

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

Spring Cloud

雖然 Spring Cloud 帶有「Cloud」,可是它並非針對雲計算的解決方案,而是在 Spring Boot 基礎上構建的,用於快速構建分佈式系統的通用模式的工具集。

使用 Spring Cloud 開發的應用程序很是適合在 Docker 或者 PaaS 上部署,因此又叫雲原生應用。雲原生能夠簡單理解爲面向雲環境的軟件架構。

既然是工具集,那麼它必定包含不少工具,咱們來看下面這張圖:

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

這裏因爲僅涉及到 API 網關的對比,所以我不逐一介紹其餘工具了。

Spring Cloud 對 Zuul 進行了整合,但從 Zuul 來看,沒有大變化,可是 Spring Cloud 整個框架通過了組件的集成,提供的功能遠多於 Netflix Zuul,可能對比時會出現差別。

Service Mesh 之 Linkerd

我想 Turgay Celik 博士把 Linkerd 做爲對比對象之一,多是由於 Linkerd 爲雲原生應用提供彈性的 Service Mesh,而 Service Mesh 可以提供輕量級高性能網絡代理,而且也提供微服務框架支撐。

從介紹來看,linkerd 是咱們面向微服務的開源 RPC 代理,它直接立足於 Finagle(Twitter 的內部核心庫,負責管理不一樣服務間之通訊流程。事實上,Twitter 公司的每一項在線服務都立足於 Finagle 構建而成,並且其支持着每秒發生的成百上千萬條 RPC 調用)構建而成,設計目標在於幫助用戶簡化微服務架構下的運維,它是專用於處理時間敏感的服務到服務的通訊基礎設施層。

和 Spring Cloud 相似,Linkerd 也提供了負載均衡、熔斷機器、服務發現、動態請求路由、重試和離線、TLS、HTTP 網關集成、透明代理、gRPC、分佈式跟蹤、運維等諸多功能,功能是至關全了,爲微服務框架的技術選型又增長了一個。因爲沒有接觸過 Linkerd,因此暫時沒法從架構層面進行分析,後續會補充這方面的內容,本身來作一次技術選型。

性能測試結果

Turgay Çelik 博士的那篇文章裏使用了 Apache 的 HTTP 服務器性能評估工具 AB 做爲測試工具。注意,因爲他是基於亞馬遜(AWS)公有云的進行的測試,可能和你實際物理機上的測試結果有出入。

實驗中啓動了客戶端和服務端兩臺機器,分別安裝多個待測試服務,客戶端經過幾種方式分別訪問,嘗試獲取資源。測試方案以下圖所示:

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

Turgay Çelik 博士的此次測試選擇了三個環境,分別是:

  1. 單 CPU 核,1GB 內存:用於比較 Nginx 反向代理和 Zuul(去除第一次運行後的平均結果);
  2. 雙 CPU 核,8GB 內存:用於比較 Nginx 反向代理和 Zuul(去除第一次運行後的平均結果);
  3. 8 個核 CPU,32GB 內存:用於比較 Nginx 反向代理、Zuul(去除第一次運行後的平均結果)、Spring Cloud Zuul、Linkerd。

測試過程均採用 200 個並行線程發送總共 1 萬次請求,命令模板以下所示:

ab -n 10000 -c 200 HTTP://<server-address>/<path to resource>

注意:因爲 Turgay Çelik 博士的測試過程當中是基於 Zuul 1 進行的測試,因此性能上較差,不能真實反映當前 Zuul 版本的性能情況,後續文章我會本身作實驗併發布結果。

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

API網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. LinkerdAPI網關性能比較:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd

從上面的結果來看,單核環境下,Zuul 的性能最差(950.57 次 /s),直接訪問方式性能最好(6519.68 次 /s),採用 Nginx 反向代理方式較直接訪問方式損失 26% 的性能(4888.24 次 /s)。在雙核環境下,Nginx 的性能較 Zuul 性能強接近 3 倍(分別是 6187.14 次 /s 和 2099.93 次 /s)。在較強的測試環境下(8 核),直接訪問、Nginx、Zuul 差距不大,可是 Spring Cloud Zuul 可能因爲內部總體消耗,致使每秒的請求數只有 873.14。

最終結論

從產品思惟來看,API 網關負責服務請求路由、組合及協議轉換。客戶端的全部請求都首先通過 API 網關,而後由它將請求路由到合適的微服務。API 網關常常會經過調用多個微服務併合並結果來處理一個請求,它能夠在 Web 協議(如 HTTP 與 WebSocket)與內部使用的非 Web 友好協議之間轉換,因此說做用仍是很大的,所以技術方案選型對於整個系統來講也有必定重要性。

從我所理解的這四款組件的設計原理來看,Zuul1 的設計模式和 Nigix 較像,每次 I/O 操做都是從工做線程中選擇一個執行,請求線程被阻塞直到工做線程完成,可是差異是 Nginx 用 C++ 實現,Zuul 用 Java 實現,而 JVM 自己有第一次加載較慢的狀況。Zuul2 的性能確定會較 Zuul1 有較大的提高,此外,Zuul 的第一次測試性能較差,可是從第二次開始就行了不少,多是因爲 JIT(Just In Time)優化形成的吧。而對於 Linkerd,它自己是對於資源比較敏感的一種網關設計,因此在通用環境下拿它和其餘網關實現相比較,可能會出現不許確的結果。

做者介紹

周明耀,畢業於浙江大學,工學碩士。13 年軟件開發領域工做經驗,10 年技術管理經驗,4 年分佈式軟件開發經驗,提交發明專利 17 項。著有《大話 Java 性能優化》、《深刻理解 JVM&G1 GC》、《技術領導力 程序員如何才能帶團隊》。微信號 michael_tec,微信公衆號「麥克叔叔每晚 10 點說」。

英文原文連接以下: https://engineering.opsgenie.com/comparing-api-gateway-performances-nginx-vs-zuul-vs-spring-cloud-gateway-vs-linkerd-b2cc59c65369

感謝郭蕾對本文的策劃和審校。

原文地址:https://www.infoq.cn/article/comparing-api-gateway-performances/
相關文章
相關標籤/搜索