I/O模型之四:Java 淺析I/O模型(BIO、NIO、AIO、Reactor、Proactor)


目錄:

I/O模型之一:Unix的五種I/O模型

I/O模型之二:Linux IO模式及 select、poll、epoll詳解

I/O模型之三:兩種高性能 I/O 設計模式 Reactor 和 Proactor

I/O模型之四:Java 淺析I/O模型

一、傳統的BIO

網絡編程的基本模型是Client/Server模型,也就是兩個進程之間進行相互通信,其中服務端提供位置信息(綁定的IP地址和監聽端口),客戶端通過連接操作向服務端監聽的地址發起連接請求,通過三次握手建立連接,如果連接建立成功,雙方就可以通過網絡套接字(Socket)進行通信。

在基於傳統同步阻塞模型開發中,ServerSocket負責綁定IP地址,啟動監聽端口,Socket負責發起連接操作,連接成功之后,雙方通過輸入和輸出流進行同步阻塞式通信。

1.1、BIO通信模型圖

首先,我們通過下面的通信模型圖來熟悉下BIO的服務端通信模型:采用BIO通信模型的服務端,通常由一個獨立的Acceptor線程負責監聽客戶端的連接,它接收到客戶端連接請求之后為每個客戶端創建一個新的線程進行鏈路處理,處理完成之后,通過輸出流返回應答給客戶端,線程銷毀。這就是典型的一請求一應答通信模型。

  該模型最大的問題就是缺乏彈性伸縮能力,當客戶端並發訪問量增加后,服務端的線程個數和客戶端並發訪問數呈1:1的正比關系,由於線程是JAVA虛擬機非常寶貴的系統資源,當線程數膨脹之后,系統的性能將急劇下降,隨着並發訪問量的繼續增大,系統會發生線程堆棧溢出、創建新線程失敗等問題,並最終導致進程宕機或者僵死,不能對外提供服務。

它的弊端有很多:

1.性能問題:一連接一線程模型導致服務端的並發接入數和系統吞吐量受到極大限制;

2.可靠性問題:由於I/O操作采用同步阻塞模式,當網絡擁塞或者通信對端處理緩慢會導致I/O線程被掛住,阻塞時間無法預測;

3.可維護性問題:I/O線程數無法有效控制、資源無法有效共享(多線程並發問題),系統可維護性差;

二、偽異步IO編程

為了解決同步阻塞IO面臨的一個鏈路需要一個線程處理的問題,后來有人對它的線程模型進行了優化,后端通過一個線程池來處理多個客戶端的請求接入,形成客戶端個數M:線程池最大線程數N的比例關系,其中M可以遠遠大於N,通過線程池可以靈活的調配線程資源,設置線程的最大值,防止由於海量並發接入導致線程耗盡。 下面,我們結合連接模型圖和源碼,對偽異步IO進行分析,看它是否能夠解決同步阻塞IO面臨的問題。

2.1、偽異步IO模型圖

采用線程池和任務隊列可以實現一種叫做偽異步的IO通信框架,它的模型圖如下:

當有新的客戶端接入的時候,將客戶端的Socket封裝成一個Task(該任務實現java.lang.Runnable接口)投遞到后端的線程池中進行處理,JDK的線程池維護一個消息隊列和N個活躍線程對消息隊列中的任務進行處理。由於線程池可以設置消息隊列的大小和最大線程數,因此,它的資源占用是可控的,無論多少個客戶端並發訪問,都不會導致資源的耗盡和宕機。 

 三、NIO(多路復用器Selector)

  Java NIO的實現關鍵是通過多路復用IO技術實現的,多路復用的核心就是通過Selector來輪詢注冊在其上的Channel,當發現某個或者多個Channel處於就緒狀態后,從阻塞狀態返回就緒的Channel的選擇鍵集合,進行IO操作。由於多路復用器是NIO實現非阻塞IO的關鍵,它又是主要通過Selector實現。

 選擇器使得我們能夠通過較少的線程便可監視許多連接上的事件。

 

詳細見《Java NIO系列教程(一) Java NIO 概述

 四、AIO異步IO

詳細見《Java NIO系列教程(八)JDK AIO編程

 

 

五、五種IO模型

  在《Unix網絡編程》一書中提到了五種IO模型,分別是:阻塞IO、非阻塞IO、多路復用IO、信號驅動IO以及異步IO。

  見《I/O模型之一:Unix的五種I/O模型

六、兩種I/O多路復用模式:Reactor和Proactor

因此便出現了下面的兩種高性能IO設計模式:Reactor(反應堆)和Proactor(前攝器)

見《I/O模型之三:兩種高性能 I/O 設計模式 Reactor 和 Proactor》和《Netty中的三種Reactor(反應堆)

  在Reactor模式中,會先對每個client注冊感興趣的事件,然后有一個線程專門去輪詢每個client是否有事件發生,當有事件發生時,便順序處理每個事件,當所有事件處理完之后,便再轉去繼續輪詢,如下圖所示:

從這里可以看出,上面的五種IO模型中的多路復用IO就是采用Reactor模式。注意,上面的圖中展示的 是順序處理每個事件,當然為了提高事件處理速度,可以通過多線程或者線程池的方式來處理事件。

可以看jdk1.8的源碼,java的nio采用的是epoll(linux,window是IOCP),也即是Reactor模式:

package sun.nio.ch;
public class DefaultSelectorProvider {
    //...    
    /**
     * Returns the default SelectorProvider.
     */
    public static SelectorProvider create() {
        String osname = AccessController
            .doPrivileged(new GetPropertyAction("os.name"));
        if (osname.equals("SunOS"))
            return createProvider("sun.nio.ch.DevPollSelectorProvider");
        if (osname.equals("Linux"))
            return createProvider("sun.nio.ch.EPollSelectorProvider");
        return new sun.nio.ch.PollSelectorProvider();
    }
    //...
}

更詳細的分析見:https://zhuanlan.zhihu.com/p/27441342?group_id=859562548406677504

在Proactor模式中,當檢測到有事件發生時,會新起一個異步操作,然后交由內核線程去處理,當內核線程完成IO操作之后,發送一個通知告知操作已完成,可以得知,異步IO模型采用的就是Proactor模式

見:

I/O模型之三:兩種高性能 I/O 設計模式 Reactor 和 Proactor

【轉】第8章 前攝器(Proactor):用於為異步事件多路分離和分派處理器的對象行為模式

Java NIO系列教程(八)JDK AIO編程

 


免責聲明!

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



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