下面僅講解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的方式