作者 | 李昂

來源 | 微服務蜂巢公衆號

本文對幾種流行的 API 網關以關鍵指標 RPS 爲依據,利用 wrk 做出性能測評並且給出結論。本文所有使用的軟件、命令、以及代碼均在文中註明,以便讀者搭建本地環境進行測試。注意性能測試的數據在不同的運行環境中差別較大,但總體上來說各項數據會成比例變化,本文的測試過程和結論可以較準確地反應出各 API 網關之間的性能差異。

背景知識介紹

API 網關

近些年來,在雲時代的背景下,爲了適應互聯網和大數據的高速發展,隨着微服務架構的持續火熱,對 API 網關的訴求越來越強烈,API 網關的產品也層出不窮。除了傳統的 Zuul 和 SpringCloud Gateway, 還誕生了很多優秀的網關,本文選取了 Edge Service 作爲比較對象與傳統的網關進行了 API 網關的性能測評。

究竟是久經沙場的老牌網關更經得起考驗,還是新興的網關性能更優?本文將給出詳細的測評過程和結果。

Netflix Zuul

Zuul 在這三個網關中是最早誕生的,其 github repo 早在 2013 年之前就已經存在,同年開始進入大衆視野,嶄露頭角。雖然 Zuul 誕生較早,也佔據着不小的市場份額,但由於 Zuul 本身是基於阻塞 io 開發的,在如今的網關市場上,相較其他的產品,性能上稍顯馬力不足。

Spring Cloud Gateway

Gateway 建立在 Spring Framework 5,Project Reactor 和 Spring Boot 2 上,不同於 Zuul 的阻塞 IO,Gateway 使用的是非阻塞 IO,相較 Zuul 具備更好的內核性能;同時與 Spring 緊密集成,對於開發者而言,成爲了一個整合方便,使用方便,性能高的產品,有着良好的生態市場作爲依託。

其實,Spring Cloud 最開始是整合 Zuul 作爲網關解決方案,但是隨着時間的推移,BIO 的侷限性不斷暴露,捉襟見肘,Spring 開始考慮另尋他路。自此,Spring Cloud Gateway 網關亮相面世。而這一產品也確實經受住了時間的考驗,成爲了業界最佳的網關選擇之一。

ServiceComb EdgeService

EdgeService 來自於 Apache 開源項目 apache/servicecomb-java-chassis,其主項目 Apache ServiceComb 是由華爲公司於 2017 年捐獻給 Apache 孵化,並於次年 10 月 24 日宣佈畢業,也是業界首個在 Apache 孵化畢業的頂級開源微服務項目。

在如今的雲原生時代背景下, EdgService 能很好的適應發展變革與鹿場角逐,由於自帶微服務場景的基因,所以 EdgeService 天生適用於在微服務場景,並且和 ServiceComb-Java-Chassis 完美集成,更好的融入微服務項目,具體信息可以參考 EdgeService 文檔:https://docs.servicecomb.io/java-chassis/zh_CN/edge/by-servicecomb-sdk.html。

性能測試

環境準備:

硬件環境:三臺機器,分別運行服務端程序,網關程序和壓測程序

CPU: 4vCPU Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz

內存:8GB

軟件環境:

wrk

工程文件:

本文涉及到的所有代碼可從 https://github.com/AngLi2/api-gateway-benchmark 獲取:

其中:

  • origin:爲本次性能測試的被調用服務文件,測試中在 192.168.0.5:8080 端口啓動。

  • zuul: 爲 zuul 網關程序,將請求分發到 origin,測試中在 192.168.0.152:8081 端口啓動。dependency 依賴的 spring-cloud-starter-zuul 版本爲 1.4.7.RELEASE,對應的 Zuul 版本爲 1.3.1。

  • gateway: 爲 gateway 網關程序,將請求分發到 origin,測試中在 192.168.0.152:8082 端口啓動。dependency 依賴的 spring-cloud-starter-gateway 版本爲 2.1.2.RELEASE

  • edgeservice: 爲 edgeservice 網關程序,將請求分發到 origin,測試中在 192.168.0.152:8083 端口啓動。dependency 依賴的 org.apache.servicecomb:edge-core 版本爲 1.2.0.B006。

關鍵配置:

不同的網關使用的是不同的配置方法,但最終的效果一致,並不影響網關的使用和性能(並不會對網關的使用和性能造成影響)。

Netflix Zuul:

通過 application.properties 進行配置:

zuul.routes.demo.path=/*

zuul.routes.demo.url=http://192.168.0.5:8080

Spring Cloud Gateway:

通過 Bean 注入的方式進行配置:

@Bean

public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {

return builder.routes()

.route(r -> r.path("/checked-out")

.uri("http://192.168.0.5:8080/checked-out")

).build();

}

ServiceComb EdgeService

由於 EdgeService 主要是針對微服務,需要先註冊 REST 服務的 endpoint、接口契約等信息,所以這裏的配置稍顯複雜,如果直接使用 ServiceComb 的微服務解決方案(服務中心,配置中心)等,會方便很多,感興趣的同學可以參考《21 天微服務實踐》中的網關部分實踐一下。

1.根據 REST 接口編寫 Java 接口類

@Path("/checked-out")

@Api(produces = MediaType.TEXT_PLAIN)

public interface Service {

@GET String getCheckedOut();

}

2. 根據 endpoint 調用方法並 且註冊:

String endpoints="rest://192.168.0.5:8080";

RegistryUtils.getServiceRegistry().registerMicroserviceMappingByEndpoints(

"thirdPartyService",

"0.0.1",

Collections.singletonList(endpoints),

Service.class

);

3.網關配置文件(這裏的 loadbalance 只是爲了 edgeservice 調用,實際只有一個被調用實例),此處 maxPoolSize 設置成了 20,原因如下:

在沒有進行 maxPoolSize 的設置的時候,使用 netstat -apn | grep 192.168.0.5 | grep ESTA | wc -l 查詢連接數,發現只建立了 20 條連接。參考 ServiceComb 文檔,可以看出鏈接數爲 verticle-count * maxPoolSize,而 maxPoolSize 的默認值只有 5,而 verticle-count 的默認值爲:

  • 如果 CPU 數小於 8,則取 CPU 數。

  • 如果 CPU 數大於等於 8,則爲 8。

因爲測試環境的 CPU 讀數爲 4,所以總鏈接數只有 4 * 5 = 20,而 Spring Cloud Gateway 的總鏈接數測試的時候一直在 90 多,所以這裏爲了保證測評公平有效,將 maxPoolSize 設置成 20。

事實上,即使在 EdgeService 的鏈接數爲 20 的情況下,測試時也比 Spring Cloud Gateway 鏈接數 90 的表現要好一點,讀者們可以自己嘗試一下。

servicecomb:

rest:

address: 127.0.0.1:8083

client:

connection:

maxPoolSize: 20

http:

dispatcher:

edge:

default:

withVersion: false

enabled: true

prefix: rest

prefixSegmentCount: 2

測試過程:

早期方案使用 Apache Benchmark 進行壓力測試,得出的結論和預期的大相徑庭,基於 rxNetty 的 Spring Cloud Gateway 比 Zuul 表現還差一大截,而且在進行長連接測試的時候 Spring Cloud Gateway 直接卡死。

這個問題在 github 的 spring-cloud-gateway 的 Issues 區早有提及:Throughput problems when compared with Netflix Zuul and Nginx(https://github.com/spring-cloud/spring-cloud-gateway/issues/124)

Issue 被提出是因爲有人提出用 Apache Benchmark 對 Spring Cloud Gateway, Netflix Zuul 和 Nginx 進行壓測,發現結果如下:

通過 Issue 的回覆區的: Add support for HTTP 1.0 (https://github.com/reactor/reactor-netty/issues/21)關聯問題,我們可以找到很多關於 rxNetty 不支持 HTTP1.0 的 Issues,這裏列出來幾個,有興趣的讀者可以點進去看看:

  • Add HTTP 1.0 support:https://github.com/ReactiveX/RxNetty/issues/575

  • Buffering of output in Spring Web Reactive with Netty too aggressive:https://github.com/spring-projects/spring-framework/issues/19510

  • Add HTTP 1.0 support on Reactor Netty:https://github.com/spring-projects/spring-framework/issues/19531

至此,得出 Apache Benchmark 並不能很好地測試出網關的性能,轉而使用 wrk 進行測試(Spring Cloud Gateway 官方使用的性能測試工具也是這個,後文會有提及)。wrk 默認工作於長連接模式,有效地減少了斷連建連的損耗,可以比較真實地反映出網關的性能。

對原始服務進行測試:

運行命令:

wrk -t12 -c100 -d300s      http://192.168.0.5:8080/checked-out

得到結果如下:

Running 5m test @ http://192.168.0.5:8080/checked-out

12 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 2.94ms 1.18ms 56.41ms 81.59%

Req/Sec 2.76k 228.24 3.76k 72.32%

9906220 requests in 5.00m, 1.25GB read

Requests/sec: 33014.70

Transfer/sec: 4.26MB

根據測試結果,可以得到延時服務的 RPS 和平均延時爲:

  • RPS:33014.70 請求 / 秒

  • Average Latency: 2.94ms

Netflix Zuul

運行命令:

wrk -t12 -c100 -d300s      http://192.168.0.5:8081/checked-out

得到結果如下:

Running 5m test @ http://192.168.0.152:8081/checked-out

12 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 12.39ms 21.27ms 1.15s 91.90%

Req/Sec 1.10k 264.62 2.09k 72.43%

3953807 requests in 5.00m, 735.99MB read

Requests/sec: 13175.21

Transfer/sec: 2.45MB

根據測試結果,可以得到 Netflix Zuul 的 RPS 和平均延時爲:

  • RPS:13175.21 請求 / 秒

  • Average Latency: 12.39ms

在性能測試的過程中使用 top 看一下 CPU 使用情況,基本滿負載運行:

Spring Cloud Gateway

運行命令:

wrk -t12 -c100 -d300s      http://192.168.0.152:8082/checked-out

得到結果如下:

Running 5m test @ http://192.168.0.152:8082/checked-out

12 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 4.95ms 9.96ms 539.29ms 98.96%

Req/Sec 1.82k 222.74 2.39k 91.81%

6507221 requests in 5.00m, 850.19MB read

Requests/sec: 21685.14

Transfer/sec: 2.83MB

根據測試結果,可以得到 Spring Cloud Gateway 的 RPS 和平均延時爲:

  • RPS:21685.14 請求 / 秒

  • Average Latency: 4.95ms

在性能測試的過程中使用 top 看一下 CPU 使用情況,基本滿負載運行:

ServiceComb EdgeService

運行命令:

wrk -t12 -c100 -d300s      http://192.168.0.152:8083/rest/thirdPartyService/checked-out

得到結果如下:

Running 5m test @ http://192.168.0.152:8083/rest/thirdPartyService/checked-out

12 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev

Latency 3.80ms 4.67ms 300.59ms 97.98%

Req/Sec 2.27k 309.82 3.10k 86.53%

8144028 requests in 5.00m, 1.03GB read

Requests/sec: 27139.19

Transfer/sec: 3.52MB

提取 RPS 數據,可以得到 EdgeService 的測試 RPS 爲:27139.19 請求 / 秒

在性能測試的過程中使用 top 看一下 CPU 使用情況,基本滿負載運行:

測試結果:

對測試的數據進行表格分析對比,分別給出平均時延、RPS 和性能損失((原服務的 RPS - 網關的 RPS) / 原服務的 RPS)表格如下圖所示:

可以看出,在硬件環境完全相同,並且 CPU 消耗基本一致的情況下,以 RPS 爲性能指標(以性能損失爲性能指標的話,差異更大,這裏參考業界做法,以 RPS 爲指標),Spring Cloud Gateway 的性能比 Netflix Zuul 好將近一倍,而 Edge Service 是 Spring Cloud Gateway 的 1.25 倍多,這還是在 EdgeService 的鏈接數劣於 Spring Cloud Gateway 20% 左右的情況下的數據,如果將 EdgeService 的鏈接數設置和 Spring Cloud Gateway 一致,性能會相差更大,有興趣的讀者可以自己嘗試一下。

結論   

Spring Cloud Gateway 的性能比 Zuul 好基本上已經是業界公認的了,實際上,Spring Cloud Gateway 官方也發佈過一個性能測試,這裏節選如下數據:

該表格數據來自於 spring-cloud-gateway-bench:https://github.com/spencergibb/spring-cloud-gateway-bench

因爲我們的測試機器部署在同一個局域網,所以性能損失均要低於 spring-cloud-gateway-bench 官方的測試數據,但是從測試結果看來基本一致。網關的性能和其實現方式也有很大的關係:

  • Netflix Zuul: 測試版本爲 1.x,基於阻塞 io

  • Spring Cloud Gateway: 前面已經提到過,基於 RxNetty,異步非阻塞

  • ServiceComb EdgeService:爲 ServiceComb 的子項目,基於 vert.x,也是異步非阻塞

同樣基於異步非阻塞,EdgeService 的性能明顯優於 Spring Cloud Gateway,可以看出網關的性能不僅和底層實現有關,和內部實現方式和優化也有很大的關係。參考 ServiceComb 的官方文檔:https://docs.servicecomb.io/java-chassis/zh_CN,可以發現 EdgeService 還支持接入 rest 自動變成 highway 轉調,性能更高。這裏因爲協議層面不一樣,就不放出來做對比了,對性能有極致要求的可以採用這種模式。

在 2018 年終於發佈了 Zuul 2.x 之後,Netflix 給出了一個比較模糊的數據,大致 Zuul2 的性能比 Zuul1 好 20% 左右。然而從測試數據看來即使性能提升一半也完全比不上 Spring Cloud Gateway 的,更不用說 EdgeService 了。看來 Zuul 2.x 並沒有把異步非阻塞的性能發揮出來。

競爭是發展的催化劑。在這個網關服務層出不窮的年代,各公司都鉚足力氣打造自己的網關產品,儘量讓自己的產品成爲用戶的第一選擇。而廣大開發者也在享受這樣的紅利,使用高性能的網關來開發自己的應用。作爲廣大開發者的一員,我們欣然接受這樣良性競爭的出現,並且也樂於嘗試市面上出現的任何新產品,誰也說不準某一個產品以後就會成爲優選的代名詞。雖然從現在網關的性能差距看來,後發優勢明顯,但在可預見的將來,各網關遲早會到達性能瓶頸,在性能差距不大並且產品穩定之後,就會有各種差異化特性出現。而等到網關產品進入百舸爭流的時代之後,用戶就可以不再根據性能,而是根據自己的需求選擇適合的網關服務了。

參考資料

https://github.com/spring-cloud/spring-cloud-gateway/issues

https://github.com/reactor/reactor-netty/issues

https://github.com/ReactiveX/RxNetty/issues

https://github.com/spring-projects/spring-framework/issues

https://github.com/spencergibb/spring-cloud-gateway-bench

https://docs.servicecomb.io/java-chassis/zh_CN

http://www.itmuch.com/spring-cloud-sum/performance-zuul-and-gateway-linkerd/

https://blog.csdn.net/j3T9Z7H/article/details/81025180

https://www.zhihu.com/question/67498050

https://www.jianshu.com/p/52c2fd448f24

https://www.w3xue.com/exp/article/20197/48191.html

相關文章