Netty基于NIO实现,Netty在NIO之上又提供了更高层次的抽象。IO 模型是 Reactor,它是一种异步、非阻塞的事件驱动模型。
一个NIO线程+一个accept线程:
主从Reactor多线程:多个acceptor的NIO线程池用于接受客户端的连接
Netty是建立在NIO基础之上,Netty在NIO之上又提供了更高层次的抽象。在Netty里面,Accept连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理。Accept连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是NIO线程。用户可以根据实际情况进行组装,构造出满足系统需求的高性能并发模型。
零拷贝的应用程序要求内核(kernel)直接将数据从磁盘文件拷贝到套接字(Socket),而无须通过应用程序。零拷贝不仅提高了应用程序的性能,而且减少了内核和用户模式见上下文切换。
是在发送数据的时候,传统的实现方式是:
File.read(bytes) Socket.send(bytes)这种方式需要四次数据拷贝和四次上下文切换:
数据从磁盘读取到内核的read buffer数据从内核缓冲区拷贝到用户缓冲区数据从用户缓冲区拷贝到内核的socket buffer数据从内核的socket buffer拷贝到网卡接口(硬件)的缓冲区明显上面的第二步和第三步是没有必要的,通过java的FileChannel.transferTo方法,可以避免上面两次多余的拷贝(当然这需要底层操作系统支持)
调用transferTo,数据从文件由DMA引擎拷贝到内核read buffer接着DMA从内核read buffer将数据拷贝到网卡接口buffer上面的两次操作都不需要CPU参与,所以就达到了零拷贝。主要体现在三个方面:
bytebufferNetty 发送和接收消息主要使用bytebuffer,bytebuffer使用对外内存(DirectMemory)直接进行Socket读写。原因:如果使用传统的堆内存进行Socket读写,JVM会将堆内存buffer拷贝一份到直接内存中然后再写入socket,多了一次缓冲区的内存拷贝。DirectMemory中可以直接通过DMA发送到网卡接口Composite Buffers 传统的ByteBuffer,如果需要将两个ByteBuffer中的数据组合到一起,我们需要首先创建一个size=size1+size2大小的新的数组,然后将两个数组中的数据拷贝到新的数组中。但是使用Netty提供的组合ByteBuf,就可以避免这样的操作,因为CompositeByteBuf并没有真正将多个Buffer组合起来,而是保存了它们的引用,从而避免了数据的拷贝,实现了零拷贝。对于FileChannel.transferTo的使用 Netty中使用了FileChannel的transferTo方法,该方法依赖于操作系统实现零拷贝。零拷贝可视化效果更好可以参见 https://www.cnblogs.com/200911/articles/10432551.html
彻底理解Netty,这一篇文章就够了