NIO系列六:流行 NIO Framework netty 和 mina 性能测评与分析

Stella981
• 阅读 741

NIO系列6:流行 NIO Framework netty 和 mina 性能测评与分析

测试方法


采用 mina 和 netty 各实现一个 基于 nio 的EchoServer,测试在不同大小网络报文下的性能表现

测试环境


客户端-服务端:

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

cache size: 6144 KB

cpu cores:  4

jdk:        1.6.0_30-b12

network:    1000Mb

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 更优雅。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战
概述本文演示的是一个Android客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo。当前由于NIO框架的流行,使得开发大并发、高性能的互联网服务端成为可能。这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2(https://www.oschina.net/ac
Stella981 Stella981
3年前
NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示
前言NIO框架的流行,使得开发大并发、高性能的互联网服务端成为可能。这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fdocs.52im.net%2Fextend%2Fdocs%2Fs
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这