JAVA 主流I/O模型比較
同步阻塞(BIO) | 僞異步IO | 非阻塞IO(NIO) | 異步IO(AIO) | |
客戶端個數:I/O線程 | 1:1 | M:N(M可以大於N) | M:1(1個I/O線程處理多個客戶端連接) | M:0(不需要啓動額外的I/O線程,被動回調) |
I/O類型(阻塞) | 阻塞I/O | 阻塞I/O | 非阻塞I/O | 非阻塞I/O |
I/O類型(同步) | 同步I/O | 同步I/O | 同步I/O(多路複用) | 異步I/O |
API使用難度 | 簡單 | 簡單 | 非常複雜 | 複雜 |
調試難度 | 簡單 | 簡單 | 複雜 | 複雜 |
可靠性 | 非常差 | 差 | 高 | 高 |
吞吐量 | 低 | 中 | 高 | 高 |
JAVA IO主要模型
BIO模型
通常由一個獨立的Acceptor線程負責監聽客戶端的連接,它接收到客戶端連接請求之後爲每個客戶端創建一個新的線程進行鏈路處理,處理完成之後,通過輸出流返回應答給客戶端,線程銷燬。 該模型缺乏彈性伸縮能力,當客戶端並非訪問量增加,服務端的線程個數和客戶端併發訪問數呈1:1的正比關係,當線程數膨脹之後,系統性能將急劇下降,最終導致進程宕機或僵死。
異步僞IO模型
當有新的客戶端接入的時候,將客戶端的Socket封裝成一個Task投遞到後端的線程池中進行處理,JDK的線程池維護一個消息隊列和N個活躍線程對消息隊列中的任務進行處理。
“僞異步IO模型”典型的問題,當可用線程都被故障服務器阻塞時,後續所有的IO消息都將在隊列中排隊,線程池採用阻塞隊列實現,當隊列積滿後,後續入隊的操作將被阻塞,前端只有一個Accept線程接收客戶端接入,它被阻塞在線程池的同步阻塞隊列之後,新的客戶端請求將被拒絕,客戶端會發生大量連接超時。
非阻塞式IO模型(NIO)
多路複用器Selector是NIO模型的基礎,一個多路複用器Selector可以同時輪詢多個註冊在它上面的Channel,服務端只需要一個線程負責Selector的輪詢,就可以接入成千上萬的客戶端連接。
模型優點:
- NIO中Channel是全雙工(是說可以通過Channel 即可完成讀操作,也可以完成寫操作)的,Channel比流(InputStream/OutputStream)可以更好地映射底層操作系統的API(UNIX網絡編程模型中,底層操作系統的通道都是全雙工的,同時支持讀寫操作);
- 客戶端發起的連接操作是異步的,不需要像之前的客戶端那樣被同步阻塞;(此時,客戶端不依賴於服務器端,也就是說客戶端發完請求可以做其他其他事情)
- 一個Selector線程可以同時處理成千上萬個client的請求,而且性能不會隨着客戶端鏈接的增加而線性下降;原因:JDK的Selector在Linux等主流操作系統上通過epoll實現,它沒有連接句柄數的限制,適合做高性能高負載的網絡服務器方案
Reactor多線程模型
reactor將接受發送分離,client發送的請求丟到線程池中,所以後續請求不會被阻塞。
但是當用戶進一步增加的時候,Reactor會出現瓶頸!因爲Reactor既要處理IO操作請求,又要響應連接請求!爲了分擔Reactor的負擔,所以引入了主從Reactor模型!
Reactor主從模型
採用多個Reactor,每個Reactor在自己單獨線程中執行,可以並行響應多個客戶端的請求事件;mainReactor 不再是一個單獨的NIO線程,而是一個獨立的NIO線程池, 處理鏈接請求,認證,登陸等操作
- Acceptor處理完成後,將事件註冊到subReactor線程池中的某個IO線程上去,此IO線程繼續完成後面的IO操作
- Netty採用類似這種模式,boss線程池就是多個mainReactor,worker線程池就是多個subReactor。
AIO模型
NIO2.0引入了新的異步通道的概念,並提供了異步文件通道和異步套接字通道的實現。異步通道提供2種方式獲取操作結果。
NIO2.0的異步套接字通道是真正的異步非阻塞I/O,它對應UNIX網絡編程中的事件驅動I/O(AIO),它不需要通過多路複用器(Selector)對註冊的通道進行輪詢操作即可實現異步讀寫,從而簡化了NIO的編程模型。
參考:
pdf文檔