天天看點

Netty系列二 Linux IO NIO 多路複用 Reactor模式

阻塞I/O

Netty系列二 Linux IO NIO 多路複用 Reactor模式
Netty系列二 Linux IO NIO 多路複用 Reactor模式

非阻塞IO

Netty系列二 Linux IO NIO 多路複用 Reactor模式
Netty系列二 Linux IO NIO 多路複用 Reactor模式

同步

使用者程序自己去查詢資料是否就緒

異步

不用自己,核心把資料拷貝到了buffer,通知使用者程式去讀取

多路複用

Netty系列二 Linux IO NIO 多路複用 Reactor模式

多個IO事件會注冊到select上,select監聽多個IO,當有就緒的IO事件,select傳回IO的狀态,程式調用IO會阻塞

select的實作方式有如下幾種:

select
Netty系列二 Linux IO NIO 多路複用 Reactor模式

select底層,核心存儲fd使用的是數組,預設1024,不能擴容,select底層沒有開辟空間存儲fd,是以每次使用者程式都要将fd傳輸給select

poll

poll過程和select一樣,核心存儲fd使用的是連結清單

epoll
Netty系列二 Linux IO NIO 多路複用 Reactor模式

epoll底層開辟了空間使用紅黑樹會存儲fd,是以使用者态不需要拷貝fd到核心态

epoll相比select,poll的優勢

1.select/poll把fd的監聽清單放在使用者空間,由使用者空間管理,導緻在使用者空間和核心空間之間頻繁重複拷貝大量fd;epoll在核心建立fd監聽清單(實際是紅黑樹),每次通過epoll_ctl增删改即可

2.select/poll每當有fd核心事件時,都喚醒目前程序,然後周遊監聽清單全部fd,檢查所有就緒fd并傳回;epoll在有fd核心事件時,通過回調把該fd放到就緒隊列中,隻需傳回該就緒隊列即可,不需要每次周遊全部監聽fd

信号驅動
Netty系列二 Linux IO NIO 多路複用 Reactor模式

首先允許套接字使用信号驅動I/O.在這種模式下,系統調用将會立即傳回,然後程式可以處理其它的工作。當資料準備就緒的時候,系統會向程式程序發送一個SIGIO信号。我們随後就可以在信号處理函數中調用read讀取資料報,并通知主循環資料已經準備好待處理,也可以立即通知主循環,讓它讀取資料報。

異步I/O

Netty系列二 Linux IO NIO 多路複用 Reactor模式

相對于同步IO,異步IO不是順序執行的,使用者程序進行aio_read系統調用之後,無論核心資料是否準備好,都會直接傳回給使用者程序,使用者程序就可以做其他的事情。等到socket資料準備好了,核心直接複制資料給程序,然後從核心向程序發送通知。IO兩個階段,程序都是非阻塞的

BIO模式

Netty系列二 Linux IO NIO 多路複用 Reactor模式
BIO(同步阻塞)模式流程

流程:

1.伺服器端的Server是一個線程,線程中執行一個死循環來阻塞的監聽用戶端的連接配接請求和通信。

2.當用戶端向伺服器端發送一個連接配接請求後,伺服器端的Server會接受用戶端的請求,ServerSocket.accept()從阻塞中傳回,得到一個與用戶端連接配接相對于的Socket。

3.建構一個handler,将Socket傳入該handler。建立一個線程并啟動該線程,線上程中執行handler,這樣與用戶端的所有的通信以及資料處理都在該線程中執行。當該用戶端和伺服器端完成通信關閉連接配接後,線程就會被銷毀。

4.然後Server繼續執行accept()操作等待新的連接配接請求。

BIO模式優點

1.使用簡單,容易程式設計

2.在多核系統下,能夠充分利用了多核CPU的資源。即,當I/O阻塞系統,但CPU空閑的時候,可以利用多線程使用CPU資源

BIO模式缺點

1.該模式的本質問題在于嚴重依賴線程,但線程Java虛拟機非常寶貴的資源。随着用戶端并發通路量的急劇增加,線程數量的不斷膨脹将伺服器端的性能将急劇下降。

2.線程生命周期的開銷非常高。線程的建立與銷毀并不是沒有代價的。在Linux這樣的作業系統中,線程本質上就是一個程序,建立和銷毀都是重量級的系統函數。

3.資源消耗。記憶體:大量空閑的線程會占用許多記憶體,給垃圾回收器帶來壓力。;CPU:如果你已經擁有足夠多的線程使所有CPU保持忙碌狀态,那麼再建立更過的線程反而會降低性能。

4.穩定性。在可建立線程的數量上存在一個限制。這個限制值将随着平台的不同而不同,并且受多個因素制約:a)JVM的啟動參數、b)Threa的構造函數中請求的棧大小、c)底層作業系統對線程的限制 等。如果破壞了這些限制,那麼很可能抛出OutOfMemoryError異常。

5.線程的切換成本是很高的。作業系統發生線程切換的時候,需要保留線程的上下文,然後執行系統調用。如果線程數過高,不僅會帶來許多無用的上下文切換,還可能導緻執行線程切換的時間甚至會大于線程執行的時間,這時候帶來的表現往往是系統負載偏高、CPU sy(系統CPU)使用率特别高,導緻系統幾乎陷入不可用的狀态。

6.容易造成鋸齒狀的系統負載。一旦線程數量高但外部網絡環境不是很穩定,就很容易造成大量請求的結果同時傳回,激活大量阻塞線程進而使系統負載壓力過大。

7.若是長連接配接的情況下并且用戶端與伺服器端互動并不頻繁的,那麼用戶端和伺服器端的連接配接會一直保留着,對應的線程也就一直存在在,但因為不頻繁的通信,導緻大量線程在大量時間内都處于空置狀态。

BIO模式适用場景

如果你有少量的連接配接使用非常高的帶寬,一次發送大量的資料,也許典型的IO伺服器實作可能非常契合

Rector模式

Netty系列二 Linux IO NIO 多路複用 Reactor模式

基于I/O多路複用模型,多個連接配接共用一個阻塞對象,應用程式隻需要在一個阻塞對象等待,無需阻塞等待所有連接配接。當某個連接配接有新的資料可以處理時,作業系統通知應用程式,線程從阻塞狀态傳回,開始進行業務處理

單Reactor單線程

Netty系列二 Linux IO NIO 多路複用 Reactor模式
步驟

Reactor是一個線程對象,該線程會啟動事件循環,并使用Selector來實作IO的多路複用,注冊一個Acceptor事件處理器到Reactor中,Acceptor事件處理器所關注的事件是ACCEPT事件,這樣Reactor會監聽用戶端向伺服器端發起的連接配接請求事件(ACCEPT事件)。

1.Reactor對象通過Select監聽用戶端Accept事件,收到事件後通過Dispatch進行分發給對應的Acceptor進行處理。Acceptor處理器通過accept()方法得到與這個用戶端對應的連接配接(SocketChannel),然後将該連接配接所關注的READ事件以及對應的READ事件處理器注冊到Reactor中。

2.當Reactor監聽到讀寫事件時,則Reactor會分發對應的讀寫Handler處理。

3.每當處理完所有就緒的感興趣的I/O事件後,Reactor線程會再次執行select()阻塞等待新的事件就緒并将其分派給對應處理器進行處理

注意,Reactor的單線程模式的單線程主要是針對于I/O操作而言,也就是是以的I/O的accept()、read()、write()以及connect()操作都在一個線程上完成的。

優點

模型簡單,沒有多線程、程序通信、競争的問題,全部都在一個線程中完成

缺點

1.性能問題,隻有一個線程,無法完全發揮多核 CPU 的性能

2.可靠性問題,線程意外終止,或者進入死循環,會導緻整個系統通信子產品不可用

單Reactor多線程

Netty系列二 Linux IO NIO 多路複用 Reactor模式

相比于單Reactor單線程模式,單Reactor多線程增加了線程池來處理非I/O操作(業務處理),這樣能夠提高Reactor線程的I/O響應,不至于因為一些耗時的業務邏輯而延遲對後面I/O請求的處理

優點

可以充分的利用多核cpu 的處理能力

缺點

多線程資料共享和通路比較複雜,Reactor處理所有的事件的監聽和響應,在單線程運作, 在高并發場景容易出現性能瓶頸

主從Reactor多線程

Netty系列二 Linux IO NIO 多路複用 Reactor模式
步驟

1.注冊一個Acceptor事件(處理accept事件)處理器到MainReactor中,啟動MainReactor的事件循環

2.用戶端向服務端發起一個連接配接請求,MainReactor監聽到該Accept事件并将該Acceptor事件派發給Acceptor處理器進行處理。Acceptor處理器通過accept()方法得到該連接配接的SocketChannel,然後将SocketChannel通過dispatch派發給subReactor

3.SubReactor線程池配置設定一個SubReactor線程給這個SocketChannel,将這個SocketChannel的R/W事件到SubReactor的selector中。

4.當有I/O事件就緒時,相關的SubReactor就将事件派發給相應的Handler。這裡的subReactor線程隻負責完成I/O的read()操作,業務處理是線上程池中完成的。

注意,是以的I/O操作(包括,I/O的accept()、read()、write()以及connect()操作)依舊還是在Reactor線程(mainReactor線程或subReactor線程)中完成的。Thread Pool(線程池)僅用來處理非I/O操作的邏輯

優點

1.父線程與子線程的資料互動簡單職責明确,父線程隻需要接收新連接配接,子線程完成後續的業務處理。

2.父線程與子線程的資料互動簡單,Reactor主線程隻需要把新連接配接傳給子線程,子線程無需傳回資料。

繼續閱讀