python epoll多路复用技术_python:多路复用+零拷贝

作为通信模块目前比较热的2个词:零拷贝,多路复用,都是性能提升较多的词,发送方利用零拷贝技术减少内存拷贝的时空开销,提升性能,接收方利用I/O多路复用技术,加速数据接受。

零拷贝

一种高效的数据传输机制,在追求低延迟的传输场景中十分常用。同样零拷贝由于数据不再进入用户空间,直接从kernel层进行处理,故在拷贝时,需要连续处理,不能中断,同时传输的数据无法修改。

零拷贝技术主要应用在文件发送环节,在kafka、Spark、Netty等高性能组件中均有使用。以下通过数据拷贝、上下文切换、时序图进行差异化比对,从标准化通信到零拷贝Block DMA,再到Scatter/Gather,数据拷贝次数不断减少,上下文切换也不断减少,随着文件增大,性能差异越来越明显,对于CPU和IO的消耗都是成倍减少。

kafka大吞吐低性能的首选队列,内含分区并行、ISR机制、顺序写入、页缓存等机制。kafka消息设计海量数据的读写,通过零拷贝能显著降低延迟,提供性能和效率。利用java NIO API中FileChannel.transferTo()方法,实现缓存数据的零拷贝。

spark在进行内存计算时,常用零拷贝来处理溢写, Shuffle过程中的溢写逻辑。由于Shuffle过程涉及大量的数据交换,因此效率当然是越高越好。零拷贝来快速合并溢写文件的分片,有一个专门的配置项来控制是否启用零拷贝(默认当然是true)

Netty提供了零拷贝的buffer,在传输数据时,最终处理的数据会需要对单个传输的报文,进行组合和拆分,Nio原生的ByteBuffer无法做到,netty通过提供的Composite(组合)和Slice(拆分)两种buffer来实现零拷贝。Netty的接收和发送ByteBuffer使用直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用JVM的堆内存进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于使用直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。Netty的文件传输调用FileRegion包装的transferTo方法,可以直接将文件缓冲区的数据发送到目标Channel,避免通过循环write方式导致的内存拷贝问题。如图:

不同的操纵系统对内核缓冲区到Socket缓冲区的复制操作,多有不同,linux中为sendfile(),java中主要通过fileChannel.transferTo完成。

RocketMQ采用零拷贝mmap+write的方式。

ps:关于sendfile与mmap+write差异及原理,参考https://zhuanlan.zhihu.

com/p/88789697。

I/O多路复用

IO多路复用:句柄描述符发生变化后,通知进程进行处理,通过回调实现进程的多路复用。多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O多路复用就是监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

select

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。由于可提升扫描上限,轮询线性扫描性能较低,数据结构传递时开销太大。不建议应用于大并发操作。

poll

poll本质上和select没有区别,链式结构,无最大连接数限制,遍历文件描述符状态获取socket,拷贝内核等待就绪,若存在就绪资源较少时,大量请求时,内核数组会变得很大,性能会线性下降。

ps:select和poll都是轮询方式,最大的连接数有限制,都有等待拷贝到内核的开销,等待资源过多时,效率线性下降。

epoll

相对于select和poll来说,epoll更加灵活,没有描述符限制,就绪状态触发回调,同时使用mmap的零拷贝。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。回调模式,在就绪状态大量情况下,大量回调可能存在性能问题。

最终选择,在连接少、或越多情况下,select和poll的性能比epoll好,epoll回调触发较多。连接较少的情况下,适合poll,大量的情况下还是epoll好。

oup实践

oup方案中的oup-flask公共模块部分,为对select、poll、epoll及多线程、多进程均作为高复用模块,固化。关于o_server.py,分享出来:

https://kdocs.cn/l/cg4T0assBHFk

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值