NIO系列六:流行 NIO Framework netty 和 mina 性能測評與分析

NIO系列6:流行 NIO Framework netty 和 mina 性能測評與分析
算法

測試方法數據庫


採用 mina 和 netty 各實現一個 基於 nio 的EchoServer,測試在不一樣大小網絡報文下的性能表現apache






測試環境centos


客戶端-服務端:api

     model name: Intel(R) Core(TM) i5-2320 CPU @ 3.00GHz     緩存

     cache size: 6144 KB     網絡

     cpu cores:  4     框架

     jdk:        1.6.0_30-b12socket

     network:    1000Mbtcp

     memory:     -Xms256m -Xmx256m

     Linux:      centos 5.7, kernel 2.6.18-274.el5


測試工具:

     jmeter v2.4


版本:

     mina            2.0.7

     netty           3.6.2.Final



配置:

     mina   

          io-processor cpu 核數

          executor     cpu 核數

          buffer       初始 buffer 大小,設置爲 2048(2k)

     netty

          boss         netty 默認配置 1

          worker       cpu 核數

          executor     cpu 核數

     

其實,從理論上來講, echo 型的應用不配置 executor 業務執行線程池會得到更好的性能和更低的消耗,但考慮在真實業務應用中,真實的業務場景處理一般涉及各類複雜邏輯計算,緩存、數據庫、外部接口訪問,爲避 免業務執行延時阻塞 io 線程執行致使吞吐下降,一般都會分離 io 處理線程 和 業務處理線程,所以咱們的測試案例中也配置了業務執行線程池考查它們線程池的調度效能。


     mina  線程池設置 

          io processor: 

               IoAcceptor acceptor = new NioSocketAcceptor(Integer.parseInt(ioPool)); 

          executor:     

             acceptor.getFilterChain().addLast("threadPool"new ExecutorFilter(Integer.parseInt(executorPool))); 

     netty 線程池設置

          io worker:     

               new NioWorkerPool(Executors.newCachedThreadPool(), Integer.parseInt(ioPool))

          executor:      

               new OrderedMemoryAwareThreadPoolExecutor(Integer.parseInt(executorPool), 0, 0)








測試結果



mina tps cpu network io  art(average response time)
90%rt(90% response time)
1k 45024/sec
150%
50MB/sec
< 1ms
1ms
2k 35548/sec
170% 81MB/sec
< 1ms
1ms
5k 10155/sec
90%
55MB/sec
3 ms
1ms
10k  8740/sec
137% 98MB/sec 3ms 4ms
50k 1873/sec 128% 100MB/sec 16ms 19ms
100k 949/sec 128% 100MB/sec 33ms 43ms


netty tps cpu network io  art(average response time)
90%rt(90% response time)
1k 44653/sec
155%
50MB/sec
< 1ms
1ms
2k 35580/sec
175% 81MB/sec
< 1ms
1ms
5k 17971/sec
195%
98MB/sec
3 ms
1ms
10k  8806/sec
195% 98MB/sec 3ms 4ms
50k 1909/sec 197% 100MB/sec 16ms 18ms
100k 964/sec 197% 100MB/sec 32ms 45ms






測試點評


mina 和 netty 在 1k、2k、10k、50k、100k 報文大小時 tps 接近

mina 在 5k 報文時有個明顯的異常(紅色標註),tps 較低,網絡 io 吞吐較低,比較 netty 在 5k 報文的網絡 io 吞吐 98MB/sec(基本接近前兆網卡極限)

5k 報文以上基本都能壓滿網絡 io,瓶頸在 io,因此 tps 和 響應時間基本相差不大。


疑問,爲何 mina 會在 5k 報文時 io 吞吐出現明顯下降?






測試分析


經過分析 mina 和 netty 的源碼,發現處理 io 讀事件時 buffer 分配策略上,兩個框架有一些區別。

在網絡 io 處理上,程序每次調用 socket api 從 tcp buffer 讀取的字節數是隨時變化的,它會受到報文大小,操做系統 tcp buffer 大小、tcp 協議算法實現、網絡鏈路帶寬各類因素影響。

所以 NIO 框架在處理每一個讀事件時,也須要每次動態分配一個 buffer 來臨時存放讀到的字節,buffer 分配的效能是影響網絡 io 框架程序性能表現的關鍵因素。


下面分別分析下 mina 和 netty buffer 的動態分配實現

mina

     buffer 分配方式:

     默認實現採用了 HeapByteBuffer,每次都是直接調用  ByteBuffer.allocate(capacity) 直接分配

     

     buffer 分配大小預測:

     根據每次讀事件實際讀到的字節數計算分配 buffer 的大小,若實際讀到字節將 ByteBuffer 裝滿,說明來自網絡的數據量可能較大而分配 buffer 容量不足,則擴大 buffer 一倍。

     若連續 2 次讀到的實際字節數小於 buffer 容量的一半,則縮小 buffer 爲原來的一半


netty

     buffer 分配方式

     默認實現採用了 DirectByteBuffer,而且實現了 buffer cache,只要 buffer 大小不改變會重複利用已經分配的 buffer

     

     buffer 分配大小預測:

     初始化了一張 buffer size 靜態分配表以下(截取部分),假如當前默認 buffer 爲 2048

     [1024, 1152, 1280, 1408, 1536, 1664, 1792, 1920, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096]

                                           |      |    |                       |

                                           A      D    B                       C

        

     根據每次讀事件實際讀到的字節數,進行預測下一次應該分配的 buffer 大小。

     若實際讀到的字節數大於等於當前 buffer(上圖 B 位置) 則將當前 buffer 增大到上圖示 C 位置,每次增大步進爲 4

     若連續 2 次實際讀到的字節數小於等於 A 位置指示的 buffer 大小,則縮小 buffer 到 D 位置

     

從上面的對比分析能夠看出,mina 採用了相對簡單的 buffer 分配和預測方式,buffer 的增加和縮小比例相同。

而 netty 採用了一種相對複雜點的 buffer 分配方式,buffer increment faster decrement slower。

事實證實 netty 的分配方式更有效的避免的 buffer 分配中的抖動問題(忽大忽小),而 buffer 分配抖動正是影響 io 吞吐的罪魁禍首。

mina 測試中 5k 報文正好產生了 buffer 分配抖動致使 io 吞吐大受影響,經過修改 mina 源碼採用固定 buffer 大小來測試則有效避免了 buffer 抖動,io 吞吐也恢復正常。

但實際狀況下,固定 buffer 確定不是有效的方式,不能很好的適應各類網絡環境的複雜性,但採用動態 buffer 分配時算法需首要考慮避免抖動。


另外能夠看出 netty 的 cpu 消耗明顯高出 mina 很多,懷疑 netty 採用的 executor 實現(OrderedMemoryAwareThreadPoolExecutor)存在比較多的鎖競爭和線程上下文切換。

下面是一組不使用 executor 時 netyy 的測試數據,其餘指標都差很少但 cpu 明顯降低很多

netty cpu
1k 75%
2k 125%
5k 126%
10k 126%
50k 118%
100k 116%







測試總結


mina:   需進一步優化其 buffer 分配,避免分配抖動致使的 io 吞吐波動

netty: 需進一步優化默認 executor 的實現,下降 cpu 消耗


其實 netty2 和 mina 都出自同一人 Trustin Lee,後來他轉投 apache 項目組將 netty 交給了社區繼續維護,以後從新設計 mina 這個框架。

從使用者感覺上來講 mina 的 api 接口設計明顯比 netty 更優雅。

相關文章
相關標籤/搜索