Netty-在NIO基础上的优化

发布时间 2023-12-13 23:38:23作者: 轻寒

零拷贝

零拷贝指的是,应用程序在需要把内核中的一块区域数据转移到另外一块内核区域去时,不需要经过先复制到用户空间,再转移到目标内核区域去了,而直接实现转移。

  1. 在网络通信上,使用直接内存。Netty 接收和发送 ByteBuf 采用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。
  2. 在缓存操作上Netty 提供了 CompositeByteBuf 类。它可以将多 Bteuf 合并为一个逻辑上的 ByteBuf,合并的过程其实仅仅是多个 ByteBuf 对象指针的映射,而不存在各个 ByteBuf 之间的拷贝。
  3. 在缓存操作上,Nety 的静态工具类 Unpooled 提供了 wrappedBuffer 方法。它可以将 bytel]、ByteBuf ByteBufer 等包装成个Netty ByteBuf 对象,包装过程中不会产生内存拷贝。
  4. 在缓存操作上,Netty 的 ByteBuf 还提供了 slice() 方法。它可以将 Byteuf 分解为多个共享同一个存储区域的 ByteBuf,从而避免了内存间的拷贝。
  5. 在文件传输上,Netty 通过 FileRegion 实现文件传输。而 FileRegion 其实正是基于 Java NI0 中的 FleChannel.transferTo)方法实现的,该方法可以将文件缓冲区的数据直接传输到目标 Channel,避免了内核缓存区和用户缓冲区之间的数据拷贝。另外,FileChannel.transferTo() 方法底层是基于 Linux 的 sendFile 函数实现的零拷贝

高性能无锁队列

Netty 中就采用了串行无锁化设计,在IO线程内部进行串行操作,避免多线程竞争导致的性能下降。表面上看,串行化设计似乎CPU利用率不高。但是,我们完全可以通过调整 NIO 线程池的线程参数,同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程的模型性能更优

具体来说,Netty 中的串行无锁化设计体现下如下2个方面

  1. Nety 是基于 Reactor 模型通过多路复用器接收并处理用户请求的。NIO 中的多路复用技术本身就是一种无锁串行化的设计思想
  2. Netty 中的 NioEventLoop 读取到消息之后,会执行 ChannelPipeline 中的 Handler,期间只要用户不主动切换线程,该用户的Handler 就会一直被同一个 NioEventLoop 调用,即同一个线程执行,这种串行化的处理方式避免了多线程操作而导致的锁的竞争,从性能角度来看是最优的。