下面仅讲解Linux的多路复用。html
Linux的IO将全部外部设备都看做文件来操做,与外部设备的操做均可以看作文件操做,其读写都使用内核提供的系统调用,内核会返回一个文件描述符(fd, file descriptor),例如socket读写使用socketfd。描述符是一个索引,指向内核中一个结构体,应用程序对文件的读写经过描述符完成。java
一个基本的IO,涉及两个系统对象:调用这个IO进程的对象,系统内核,read操做发生时流程以下:linux
I/O多路复用系统开销小,系统没必要建立进程/线程,也不须要维护这些进程/线程。git
目前支持I/O多路复用的系统调用包括select,pselect,poll,epoll,I/O多路复用即经过一种机制,一个进程能够监视多个描述符,一旦某个描述符准备就绪,就可以通知程序进行相应的读写操做。github
select目前在全部平台支持,select函数监视文件操做符(将fd加入fdset),循环遍历fdset内的fd获取是否有资源的信息,若遍历完全部fdset内的fd后无资源可用,则select让该进程睡眠,直到有资源可用或超时则唤醒select进程,以后select继续循环遍历,找到就绪的fd后返回。select单个进程打开的fd有必定限制,由FD_SETSIZE设置,默认为1024(32位)和2048(64位)。编程
poll与select的主要区别是不使用fdset,而是使用pollfd结构(本质链表结构),于是没有fd数目限制。windows
poll和select共有的问题:数组
Linux 2.6内核中提出了epoll,epoll包括epoll_create,epoll_etl,epoll_wait三个函数分别负责建立epoll,注册监听的事件和等待事件产生。promise
epoll有LT模式和ET模式:缓存
使用Netty而非直接使用Java NIO出于如下缘由:
下面简单介绍下Netty的部分功能。
Netty的ByteBuf依然是Byte数组缓冲区,提供对基础类型,byte[]数组,ByteBuffer,ByteBuf的读写,缓冲区自身的copy和slice,操做指针,字节序,构造实例等功能。相对于ByteBuffer,ByteBuf的读写采用两个指针而非flip方案,增长了可靠性,并提供了自动扩展方案。
ByteBuf的内存池实现比较复杂,可是否使用内存池,有较大的性能差别。随着JVM和JIT的发展,对象的分配和回收是个轻量级的工做,可是对于缓冲区Buffer,特别是堆外直接内存的分配和回收则仍很耗时。Netty提供了基于内存池的缓冲区重用机制,带来了性能提升。UnpooledByteBufAllocator在Netty4仍然是默认的allocator,但在大多状况下,PooledByteBufAllocator将带来更高性能。更改默认方式仅需在初始化时加以设置:
客户端
b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
服务端
.childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.config().setAllocator(PooledByteBufAllocator.DEFAULT);
Netty的Channel和NIO的Channel相似,但有本身的子类和实现。Unsafe则封装了Netty不但愿用户调用的API,做为Channel的辅助类。
Channel包括而不限于网络的读,写,客户端发起链接,主动关闭链接,链路关闭,获取双方通讯地址等功能。Channel也包括了Netty框架的相关功能,如获取该Channel的EventLoop,获取缓冲区分配器ByteBufAllocator和Pipeline等。Channel封装了Java NIO不统一的SocketChannel和ServerSockerChannel,其接口定义大而全。
Unsafe是Channel的辅助接口,实际的I/O读写操做都是由Unsafe完成的。包括register,bind,disconnect,close,write,flush几个接口,能够看到它更接近于本来的Java NIO Channel。
Netty的pipeline和handler机制相似于Servlet和Filter,为了方便拦截和业务逻辑定制。Netty将Channel的管道抽象为ChannelPipeline,让消息在其中流动,ChannelPipeline持有消息拦截器ChannelHandler列表,能够经过增长和删除handler来改变业务逻辑,而不是对已有的handler进行修改。
ChannelHandler的种类繁多,且用户能够自定义,自定义时,一般只须要继承ChannelHandlerAdapter并重写为了实现业务逻辑的必要方法便可。
此外,ChannelPipeline支持运行时动态添加或删除ChannelHandler,某些场景下这个特性很实用。
ChannelPipeline是线程安全的,但ChannelHandler不是线程安全的,须要用户本身进行保障。
Netty的线程模型得以无锁化依赖于其NioEventLoop。所以,此处详细展开。
Netty线程池:服务端启动时,建立bossGroup, workerGroup两个NioEventLoopGroup,其实是两个Reactor线程池,一个用于接收客户端TCP请求,一个用于处理I/O读写或执行业务。
经过调整bossGroup和workerGroup的线程个数,group()函数参数数量,是否共享线程池等,Netty的Reactor模型能够在单线程,多线程,主从多线程等模式中切换。
Netty的NioEventLoop读取到消息以后,直接调用ChannelPipeline的fireChannelRead方法,只要用户不切换线程,一直都由NioEventLoop调用用户的Handler,期间不切换线程,而是串行化运行handler,避免了多线程操做的锁的竞争,达到性能最优。
NioEventLoop不纯粹是一个IO线程,它既能够处理系统Task又能够处理定时任务。
Future起源于JDK的Future,Netty的Future命名为ChannelFuture,与Channel操做有关。Netty中全部操做都是异步的,所以,获取异步操做结果,就要交给ChannelFuture。ChannelFuture有completed何uncompleted两种状态,建立后处于uncompleted状态,一旦I/O操做完成,则被设置成completed状态,此时可能操做失败,操做成功或操做被取消。和JDK的Future相似,ChannelFuture有不少方便的API,包括获取操做结果,添加事件监听器,取消I/O操做,同步等待等。
Promise是可写的Future,用于设置I/O额结果。Netty发起I/O操做时,会建立一个新的Promise对象。
聊聊IO多路复用之select、poll、epoll详解
关于同步,异步,阻塞,非阻塞,IOCP/epoll,select/poll,AIO ,NIO ,BIO的总结
【Java】从BIO、NIO到Linux下的IO多路复用
OSX/iOS中多路I/O复用总结
java nio及操做系统底层原理
Select函数实现原理分析
Netty4底层用对象池和不用对象池实践优化
设置Netty接收Buff为堆内存模式
关于java nio在windows下实现
NIO.2 uses IOCP
在 Java 7 中体会 NIO.2 异步执行的快乐
Java IO & NIO & NIO2
5种调优Java NIO和NIO.2的方式