基於Netty的四層和七層代理性能方面的一些壓力測試

本文咱們主要是想測試和研究幾點:nginx

  • 基於Netty寫的最簡單的轉發HTTP請求的程序,四層和七層性能的差別
  • 三種代理線程模型性能的差別,下文會詳細解釋三種線程模型
  • 池和非池化ByteBuffer性能的差別

本文測試使用的代碼在: github.com/JosephZhu19…git

在代碼裏咱們實現了兩套代理程序: github

image_1demdig32ia6184m64sppm8vp90.png-55.9kB

測試使用的機器配置是(阿里雲ECS): spring

image_1dembkev02d2sll1ijc18fl4r48j.png-91.9kB
一共三臺機器:

  • server 服務器安裝了nginx,做爲後端
  • client 服務器安裝了wrk,做爲壓測客戶端
  • proxy 服務器安裝了咱們的測試代碼(代理)

Nginx後端

nginx 配置的就是默認的測試頁(刪了點內容,減小內網帶寬): 後端

image_1dembfnk81i9m19tkvli148c13h86.png-122.8kB
直接對着nginx壓測下來的qps是26.6萬:
image_1delvmebjcpe39n1hdni41hss13.png-55.2kB

有關四層和七層

四層的代理,咱們僅僅是使用Netty來轉發ByteBuf。 七層的代理,會有更多額外的開銷,主要是Http請求的編碼解碼以及Http請求的聚合,服務端:性能優化

image_1demdm2m82vg1i6b4ng1uitjcp9d.png-136.8kB

客戶端: 服務器

image_1demdoius2ekjds1kbr5a1vld9q.png-63.2kB

這裏咱們能夠想到,四層代理由於少了Http數據的編解碼過程,性能確定比七層好不少,好多少咱們能夠看看測試結果。網絡

有關線程模型

咱們知道做爲一個代理,咱們須要開啓服務端從上游來獲取請求,而後再做爲客戶端把請求轉發到下游,從下游獲取到響應後,返回給上游。咱們的服務端和客戶端都須要Worker線程來處理IO請求,有三種作法;app

  • A:客戶端Bootstrap和服務端ServerBootstrap獨立的線程池NioEventLoopGroup,簡稱IndividualGroup
  • B:客戶端和服務端共享一套線程池,簡稱ReuseServerGroup
  • C:客戶端直接複用服務端線程EventLoop,簡稱ReuseServerThread

以七層代理的代碼爲例: oop

image_1demdqavbn5i19ff1g1hrp2gbsan.png-98.4kB

接下去的測試咱們會來測試這三種線程模型,這裏想固然的猜想是方案A的性能是最好的,由於獨立了線程池不相互影響,咱們接下去看看結果

四層代理 + ReuseServerThread線程模型

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)

image_1delvsom6v03e5pngacv714901g.png-54kB

四層代理 + IndividualGroup線程模型

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)

image_1dem04l2alqs1l4u1ripg9a1fcu1t.png-54.8kB

四層代理 + ReuseServerGroup線程模型

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)

image_1dem0br3r1rr3qmj1mk519nn111v2a.png-55.2kB

看到這裏其實已經有結果了,ReuseServerThread性能是最好的,其次是ReuseServerGroup,最差是IndividualGroup,和咱們猜的不一致。

四層系統監控圖

從網絡帶寬上能夠看到,先測試的ReuseServerThread跑到了最大的帶寬(後面三個高峯分別表明了三次測試):

image_1dem0chjrimkn5va5810dk1vk62n.png-52.8kB
從CPU監控上能夠看到,性能最好的ReuseServerThread使用了最少的CPU資源(後面三個高峯分別表明了三次測試):
image_1dem0ekoq1l59ju1vvn1lp575u34.png-32.5kB

七層代理 + ReuseServerThread線程模型

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)

image_1dem0mduhkdc11hc2ue12rd433h.png-55kB

七層代理 + IndividualGroup線程模型

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)

image_1dem0tgtv13ev3h9sl51appi083u.png-55.2kB

七層代理 + ReuseServerGroup線程模型

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)

image_1dem14prr1e7kr0gi1ggiqu7l4b.png-55kB

結論同樣,ReuseServerThread性能是最好的,其次是ReuseServerGroup,最差是IndividualGroup。我以爲是這麼一個道理:

  • 複用IO線程的話,上下文切換會比較少,性能是最好的,後來我也經過pidstat觀察驗證了這個結論,可是當時忘記截圖
  • 複用線程池,客戶端有機會能複用到服務端線程,避免部分上下文切換,性能中等
  • 獨立線程池,大量上下文切換(觀察下來是複用IO線程的4x),性能最差

七層系統監控圖

下面分別是網絡帶寬和CPU監控圖:

image_1dem1fh7m1f0cl8s1d1ic7563765.png-39.3kB
image_1dem1e3g01asrq8r9u16ce5e94r.png-60.1kB
能夠看到明顯七層代理消耗更多的資源,可是帶寬相比四層少了一些(QPS少了不少)。 出流量比入流量多一點,應該是代碼裏多加的請求頭致使:
image_1demf0bhrikp1rh0r5i1q3c1iltc1.png-150.8kB

試試HttpObjectAggregator設置較大maxContentLength

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)

image_1dem1qe4v1ddd1c2311pjej81bf16v.png-54.9kB

試試PooledByteBufAllocator

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)

image_1dem1ifds1hoi1lkka691vekmlt6i.png-54.8kB

能夠看到Netty 4.1中已經把默認的分配器設置爲了PooledByteBufAllocator

image_1demg35il1ambhdb1o3m42c1j9ce.png-43.9kB

總結

這裏總結了一個表格,性能損失比例都以第一行直接壓Nginx爲參照:

image_1demepcbume4eoacntrb11mh2b4.png-39.1kB

結論是:

  • Nginx很牛,其實機器配置不算太好,在配置比較好的物理機服務器上跑的化,Nginx單機百萬沒問題
  • Netty很牛,畢竟是Java的服務端,四層轉發僅損失3% QPS
  • 無論是七層仍是四層,複用線程的方式明顯性能最好,佔用CPU最少
  • 由於上下文切換的緣由,使用Netty開發網絡代理應該複用IO線程
  • 七層的消耗比四層大不少,即便是Netty也避免不了,這是HTTP協議的問題
  • PooledByteBufAllocator性能比UnpooledByteBufAllocator有必定提高(接近3%)
  • HttpObjectAggregator若是設置較大的最大內容長度,會略微影響點性能

之因此寫這個文章作這個分析的緣由是由於最近在作咱們自研網關的性能優化和壓力測試 github.com/spring-aven… 。 我發現有一些其它開源的基於Netty的代理項目並非複用鏈接的,可能做者沒有意識到這個問題,我看了下Zuul的代碼,它也是複用的。

相關文章
相關標籤/搜索