簡單聊下IO復用


沒圖,不分析API

 

Java中IO API的發展:
Socket -> SocketChannel -> AsynchronousSocketChannel
ServerSocket -> ServerSocketChannel -> AsynchronousServerSocketChannel

同步/阻塞 -> 同步/非阻塞(多路復用) -> 異步

 

想簡單聊下多路復用。多路復用需要配合Reactor模式,前者解決技術上的問題,后者解決軟件工程的問題。

技術上的問題,是將IO操作中等待和非等待的部分分開處理。我們都知道IO操作分為兩個部分:
1、等待數據就緒
2、處理數據

 

眾所周知的幾種IO模型(阻塞、非阻塞、多路復用、信號驅動、異步)就是區別於這兩個階段,當需要處理很多連接的時候(高並發的情況),容易想到的是使用多線程技術,比如最簡單的One-connection-Per-thread模式,但是因為等待數據不可避免,造成的結果是線程不停的休眠-喚醒的切換,導致CPU不堪重負。

IO復用的目的:將這兩個階段分開處理,讓一個線程(而且是內核級別的線程)來處理所有的等待,一旦有相應的IO事件發生就通知繼續完成IO操作,雖然仍然有阻塞和等待,但是等待總是發生在一個線程,這時使用多線程可以保證其他線程一旦喚醒就是處理數據,當然這需要非阻塞IO API的支持(比如非阻塞套接字)。Linux2.6之前的select,poll以及之后的epoll都是IO復用技術的實現。selectpoll基本一致,epoll是對它們的改進版本。但總的來說它們都還不是真正的異步IO,因為它們在IO讀寫的時候仍然是阻塞的、同步的(完成一件事后才能做另外一件事)。異步IO是指“處理數據”這一階段也是非阻塞的。Windows上的IOCP(完成端口)才是真正的AIO,理論上它比Linux的epoll更先進。

至於select、poll和epoll的區別,推薦這篇文章:http://www.cnblogs.com/Anker/p/3265058.html。簡單來說:select,poll無腦的輪詢,忽略了高並發下,輪詢本身成了瓶頸,而epoll使用回調實現了輪詢真正需要處理的連接。

 

Reactor模式是為了我們更簡單的使用IO復用技術。它是一種並發IO模式,其他的模式還有多進程,多線程等。Reactor本身也有很多變種,比如thread per request,worker thread,thread pool,multiple reactors...網上這方面的資料很多。雖然網上關於reactor和多線程模孰優孰劣還有爭論(Reactor最明顯的一個缺點是無法充分利用多核的優勢),但是大部分高並發的框架或組建都是基於reactor的,比如MINA,Netty,再比如Redis,Nginx(有多個工作進程來充分利用多核的優勢)。關於Java中的IO復用可以看Doug Lea大神的Scalable IO in Java(http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf)。

至於JDK1.7中出現的Asynchronous I/O,只要是運行Linux上肯定無外乎epoll,那是不是可以說本質上仍然不是真正的異步IO呢?個人覺得異步這個概念是有粒度的,不可能做到完全的異步。JDK1.7中的AIO從編程的角度對程序員來說確是異步的,我們不用像在多路復用中那樣自己去select了,我們需要做的就是在completion handlers中處理業務邏輯。

另外提一點:操作系統底層的IO操作都是異步的——IO中斷,只不過同步更符合正常人的思維,更易於理解。

 

最后關於異步IO還想補充一點,雖然異步IO的第二階段也是非阻塞的,但是仍然有優化空間。就是在數據從內核copy到用戶空間這個過程,Netty就使用了Zero-Copy技術來優化這個步驟(http://my.oschina.net/plucury/blog/192577),另外還有MMAP。

 

歡迎斧正!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM