对标内核抓包极限提升udp收包率

    在海量数据的收集中,大部分网络日志都是通过udp协议传输的,所以为了保证日志的完整性,udp的收包性能显得极其重要,所以对此主题进行一系列的研究。

1.1 测试环境

操作系统:linux 3.10.0-229.el7.x86_64

网络带宽:千兆网

机器内存:32GB

处理器:4核

硬盘空间:1T

硬盘IO:实测300MB/S

JDK版本:1.8

 

1.2 测试用例

    1、报文大小:870b

测试对象:java udp,用户层抓包,内核抓包

测试结果:

速率PPS bio-udp 用户层收包(tcpdump) 内核收包(pf-ring)
3200 100% 100% 100%
6400 99.17% 100% 100%
13000 98.87% 99.78% 100%

 

从表格中可以看出在低速pps情况下,用户层收包和内核收包相差并不大,java的udp收包和tcpdump性能也比较接近,三者差距看不出来。


    2、报文大小125b

测试对象:syslog报文,不解格式收包,用户层收包,内核抓包

字节B 速率PPS syslog bio-udp 用户层收包(tcpdump) 内核收包(pf-ring)
125 6400 98.91% 100% 100% 100%
13000 98.18% 99.82% 100% 100%
26000 61.89% 97.44% 100% 100%
50000 32.15% 94.79% 99.71% 100%
60000 / / 99.43% 100%
70000 / / 98.73% 100%
100000 / / 76.67% 100%
200000 / / 42.06% 100%
500000 / / 37.73% 99.98%

 

降低报文大小,提高pps测试,可以发现:

1)收包率和报文大小关系不大,关键因素是报文发送速率。

2)syslog解析性能急速下降,大部分时间都花在解析工作上,导致丢包率非常高

3)内核收包在50Wpps下,依旧保持近100%的收包率,性能非常强劲

4)用户层抓包在7Wpps之后丢包率明显上升,可能受限于cpu中断

5)java 的udp收包和tcpdump差的并不多,其实原理是一样的,原因大概是日志的输出以及程序其他地方的性能损耗

6)此时,java 的udp收包最多做到1Wpps下逼近100%收包率

 

    3、报文大小50b

测试对象:用户层抓包,内核抓包

字节B 速率PPS 用户层收包(tcpdump) 内核收包(pf-ring)
50 67W 29.74% 99.46%
83W 29.63% 93.87%
100W 29.42% 79.18%

 

为了看看内核抓包性能究竟有多强悍,把速率提高到了100Wpps,发现一个有意思的事情,tcpdump的丢包率在降到30%左右,一直维持稳定,这儿我也没想明白。内核抓包在80Wpps后,收包率也有明显的下滑趋势。可能是cpu性能的原因,看来内核抓包也做不到百万级抓包,按照这个测试结果,他的性能应该就是每秒80W级别的处理速度(pf-ring的dna模式或许可以达到真正的百万级)。

 

1.2 性能优化

    通过上面的测试发现,内核抓包有着无可比拟的优势,逼近每秒百万级的性能,但是由于实际使用中,限制太大对网卡型号要求太高,需要重新编译网卡驱动,需要root的权限,c语言开发等等限制条件,导致应用到实际项目中,适用场景太少且难度较大。反观tcpdump的性能,虽然无法和pf-ring比,但是也有每秒5W的速度,然而我们的程序udp收包只有每秒4W多的速度,在5wpps收包率只有94%,这意味着每一亿条报文,就会丢失600W,还是非常吓人的,所以这方面还是非常有必要去优化提高收包率的。


优化一:

使用NIO流来处理网络消息,测试报文125b

速率PPS bio-udp nio-udp 用户层收包(tcpdump)
6400 100% 100% 100%
13000 99.82% 100% 100%
26000 97.44% 99.82% 100%
50000 94.79% 99.23% 99.71%

 

优化结果:性能提升还是非常明显的,在5Wpps下,收包率提高了近5分点,意味着一亿条可以多收500W,但仍旧有几大十万的丢失,还是没有达到tcpdump的性能

 

优化二:

使用AIO流来处理网络消息,测试报文125b

速率PPS bio-udp aio-udp 用户层收包(tcpdump)
6400 100% 100% 100%
13000 99.82% 100% 100%
26000 97.44% 99.76% 100%
50000 94.79% 99.27% 99.71%

 

优化结果:测试结果发现几乎和nio没差,看来收包层面已经达到极限了。与tcpdump依旧有0.5的差距,分析之后,定位是报文落地磁盘导致了收包过程有中断

 

优化三:

借鉴零拷贝的思想,落地磁盘也采用内存映射的方式,mappedByteBuffer

优化结果:有稍许提升,依旧距离tcpdump较远

 

优化四:

直接采用内存队列的方式,让收包线程完全脱离写磁盘的职责

速率PPS bio-udp aio-udp 内存映射+队列+bio 用户层收包(tcpdump)
6400 100% 100% 100% 100%
13000 99.82% 100% 100% 100%
26000 97.44% 99.76% 99.99% 100%
50000 94.79% 99.27% 99.69% 99.71%

 

优化结果:在26000PPS下,已经99.99%的收包率了,可以说保证能在2.5Wpps下不丢包。在5Wpps下也无限逼近tcpdump的性能,tcpdump我只测试了两次取得平均值,前者测试了6次取得平均值。先天条件如此,能用到的最新的技术全上了,到这个地步已经是极限了,不过应该可以应付99%的场景了。pf-ring以及tcpdump毕竟抓的只是pcap包,并不解析协议,所以这个已经是非常好的结果了。

 

最后附一张全测试的表格:

字节B 速率PPS syslog bio-udp nio-udp 内存映射+队列+nio 用户层收包(tcpdump) 内核收包(pf-ring)
869 3200  / 100%  /  / 100% 100%
6400  / 99.17%  /  / 100% 100%
13000  / 98.87%  /  / 99.78% 100%
              100%
393 3200  / 100%  /  / 100% 100%
6400  / 99.23%  /  / 100% 100%
13000  / 98.75%  /  / 99.99% 100%
26000  / 98.41%  /  / 99.70% 100%
               
  3200 100% 100% 100% 100% 100% 100%
125 6400 98.91% 100% 100% 100% 100% 100%
13000 98.18% 99.82% 100% 100% 100% 100%
26000 61.89% 97.44% 99.82% 99.99% 100% 100%
50000 32.15% 94.79% 99.23% 99.69% 99.71% 100%
60000 / /  /  / 99.43% 100%
70000 / /  /  / 98.73% 100%
100000 / /  /  / 76.67% 100%
200000 / /  /  / 42.06% 100%
500000 / /  /  / 37.73% 99.98%
               
50 67W / /  /  / 29.74% 99.46%
83W / /  /  / 29.63% 93.87%
100W / /  /  / 29.42% 79.18%

你可能感兴趣的:(Java)