BIO:
在此種方式下,用戶進程在發起一個IO操作以後,必須等待IO操作的完成,只有當真正完成了IO操作以後,用戶進程才能運行。JAVA傳統的IO模型屬於此種方式。
傳統的客戶端服務器就是採用此種IO方式,一個socket鏈接對應一個線程,服務器中的建立連接 “serverSocket.accept()”會一直阻塞,直到有連接接入。
NIO:
在此種方式下,用戶進程發起一個IO操作以後邊可返回做其它事情,但是用戶進程需要時不時的詢問IO操作是否就緒,這就要求用戶進程不停的去詢問,從而引入不必要的CPU資源浪費。
可以使用setsockopt()方法配置套接字,以便讀/寫調用在沒有數據的時候立即返回,也就是說:如果是一個阻塞調用應該已經被阻塞了;可以使用操作系統的事件通知API註冊一組非阻塞套接字,以確定它們中是否有任何的套接字已經有數據可供讀寫。
class java.nio.channels.Selector 是Java 的非阻塞I/O 實現的關鍵。它使用了事件通知API以確定在一組非阻塞套接字中有哪些已經就緒能夠進行I/O 相關的操作。
Netty——事件和異步驅動
一個既是異步的又是事件驅動的系統會表現出一種特殊的、對我們來說極具價值的行爲:它可以以任意的順序響應在任意的時間點產生的事件。
Netty的核心組件:
- channel:實體的開放連接,可以把Channel 看作是傳入(入站)或者傳出(出站)數據的載體。
- 回調:Netty在內部使用了回調來處理事件;當一個回調被觸發時,相關的事件可以被interface-ChannelHandler
的實現處理。 - Future:Netty提供了自己的實現——ChannelFulture,用於在執行異步操作的時候使用。Fulture需要添加監聽器ChannelFutureListener,其可以看作是回調的一個更加精細的版本。每個Netty的出站IO操作都會返回一個ChannelFuture.
- 事件和channelHandler:首先Netty中的事件是按照它們與入站或出站數據流的相關性進行分類的。下圖爲事件和channelHandler的關係。channelHandler就是事件處理器的抽象。
Netty核心組件間的關係,事件通過channel被派發給channelHandler,然後channelHandler對事件的處理和結果返回通過回調和Future來完成。對於事件的派發等相關操作被封裝到EventLoop.
EventLoop 本身只由一個線程驅動,其處理了一個Channel 的所有I/O 事件,並且在該EventLoop 的整個生命週期內都不會改變。這個簡單而強大的設計消除了你可能有的在ChannelHandler 實現中需要進行同步的任何顧慮。
Netty編寫的服務器必備:
- 至少一個——ChannelHandler,該組件實現了服務器對從客戶端接收的數據的處理,即它的業務邏輯。
- 引導—這是配置服務器的啓動代碼。至少,它會將服務器綁定到它要監聽連接請求的端口上。
ChannelHandler 和業務邏輯:
ChannelHandler,它是一個接口族的父接口,它的實現負責接收並響應事件通知。在Netty 應用程序中,所有的數據處理邏輯都包含在這些核心抽象的實現中。
實現ChannelInboundHandler接口的Echo服務器,我們感興趣的方法爲:
- channelRead()—對於每個傳入的消息都要調用;
- channelReadComplete()—通知ChannelInboundHandler 最後一次對channelRead()的調用是當前批量讀取中的最後一條消息;
- exceptionCaught()—在讀取操作期間,有異常拋出時會調用。
Netty的組件和設計
瞭解Netty中類的抽象:
- Channel—Socket;
- EventLoop—控制流、多線程處理、併發;
- ChannelFuture—異步通知。
三者的關係:
- EventLoopGroup包含一個或者多個EventLoop;
- 一個EventLoop在它的生命週期內只和一個Thread綁定;
- 所有由EventLoop處理的I/O事件都將在它專有Thread上被處理;
- 一個Channel在它的生命週期內只註冊於一個EventLoop;
- 一個EventLoop可能會被分配給一個或多Channel。
ChannelHandler 和ChannelPipeline
- ChannelHandlerNetty的主要組件,它充當了所有處理入站和出站數據的應用程序邏輯的容器。
- ChannelPipeline 提供了ChannelHandler 鏈的容器,並定義了用於在該鏈上傳播入站和出站事件流的API。當Channel 被創建時,它會被自動地分配到它專屬的ChannelPipeline。
二者的關係:
使得事件流經ChannelPipeline 是ChannelHandler 的工作。實際上,被我們稱爲ChannelPipeline 的是這些ChannelHandler 的編排順序。
注意:當ChannelHandler 被添加到ChannelPipeline 時,它將會被分配一個ChannelHandlerContext,其代表了ChannelHandler 和ChannelPipeline 之間的綁定。雖然這個對象可以被用於獲取底層的Channel,但是它主要還是被用於寫出站數據。