本文我們主要是想測試和研究幾點:
本文測試使用的代碼在: https://github.com/JosephZhu1983/proxytest
在代碼裏我們實現了兩套代理程序:
測試使用的機器配置是(阿里雲ECS):
一共三臺機器:
nginx 配置的就是默認的測試頁(刪了點內容,減少內網帶寬):
直接對著nginx壓測下來的qps是26.6萬:
四層的代理,我們僅僅是使用Netty來轉發ByteBuf。 七層的代理,會有更多額外的開銷,主要是Http請求的編碼解碼以及Http請求的聚合,服務端:
客戶端:
這裡我們可以想到,四層代理因為少了Http數據的編解碼過程,性能肯定比七層好很多,好多少我們可以看看測試結果。
我們知道作為一個代理,我們需要開啟服務端從上游來獲取請求,然後再作為客戶端把請求轉發到下游,從下游獲取到響應後,返回給上游。我們的服務端和客戶端都需要Worker線程來處理IO請求,有三種做法;
以七層代理的代碼為例:
接下去的測試我們會來測試這三種線程模型,這裡想當然的猜測是方案A的性能是最好的,因為獨立了線程池不相互影響,我們接下去看看結果
Layer4ProxyServer Started with config: ServerConfig(type=Layer4ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerThread, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Layer4ProxyServer Started with config: ServerConfig(type=Layer4ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=IndividualGroup, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Layer4ProxyServer Started with config: ServerConfig(type=Layer4ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerGroup, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
看到這裡其實已經有結果了,ReuseServerThread性能是最好的,其次是ReuseServerGroup,最差是IndividualGroup,和我們猜的不一致。
從網路帶寬上可以看到,先測試的ReuseServerThread跑到了最大的帶寬(後面三個高峯分別代表了三次測試):
從CPU監控上可以看到,性能最好的ReuseServerThread使用了最少的CPU資源(後面三個高峯分別代表了三次測試):
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerThread, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=IndividualGroup, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerGroup, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
結論一樣,ReuseServerThread性能是最好的,其次是ReuseServerGroup,最差是IndividualGroup。我覺得是這麼一個道理:
下面分別是網路帶寬和CPU監控圖:
可以看到明顯七層代理消耗更多的資源,但是帶寬相比四層少了一些(QPS少了很多)。 出流量比入流量多一點,應該是代碼裏多加的請求頭導致:
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerThread, receiveBuffer=10240, sendBuffer=10240, allocatorType=Pooled, maxContentLength=100000000)
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerThread, receiveBuffer=10240, sendBuffer=10240, allocatorType=Pooled, maxContentLength=2000)
可以看到Netty 4.1中已經把默認的分配器設置為了PooledByteBufAllocator
這裡總結了一個表格,性能損失比例都以第一行直接壓Nginx為參照:
結論是:
之所以寫這個文章做這個分析的原因是因為最近在做我們自研網關的性能優化和壓力測試https://github.com/spring-avengers/tesla。 我發現有一些其它開源的基於Netty的代理項目並不是復用連接的,可能作者沒有意識到這個問題,我看了下Zuul的代碼,它也是復用的。
推薦閱讀: