目錄
1.Channel、EventLoop和ChannelFuture
2.ChannelHandler和ChannelPipeline
2.4 抽象類SimpleChannelInboundHandler
本章我們要詳細地研究Netty的各個組件,看看它們是如何通過協作來支撐這些體系結構上的最佳實踐的。
1.Channel、EventLoop和ChannelFuture
Channel、EventLoop和ChannelFuture類合在一起,可以被認爲是Netty網絡抽象的代表:
- Channel:Socket
- EventLoop:控制流、多線程處理、併發
- ChannelFuture:異步通知
1.1 Channel接口
基本的IO操作(bind()/connect()/read()/write())依賴於底層網絡傳輸所提供的原語。在基於Java的網絡編程中,其基本的構造是class Socket。Netty的Channel接口所提供的API,大大地降低了直接使用Socket類的複雜性。此外,Channel也是擁有許多預定義的、專門化實現的廣泛類層次結構的根。Channel主要有以下具體類:
- EmbeddedChannel
- LocalServerChannel
- NioDatagramChannel
- NioSctpChannel
- NioSocketChannel
1.2 EventLoop接口
EventLoop定義了Netty的核心抽象,用於處理連接的生命週期中所發生的事件。下圖在高層次上說明了Channel、EventLoop、Thread以及EcentLoopGroup之間的關係。
這些關係是:
- 一個EventLoopGroup包含一個或者多個EventLoop
- 一個EventLoop在它的生命週期內只和一個Thread綁定
- 所有由EventLoop處理的IO事件都將在它專有的Thread上被處理
- 一個Channel在它的生命週期內只註冊於一個EventLoop
- 一個EventLoop可能會被分配給一個或多個Channel
在這種設計中,一個給定Channel的IO操作都是由相同的Thread執行的,實際上消除了對於同步的需求。
1.3 ChannelFuture接口
Netty中所有的IO操作都是異步的,因爲一個操作可能不會立即返回,所以需要一種用於在之後的某個時間點確定其結果的方法。爲此,Netty提供了ChannelFuture接口,其addListener()方法註冊了一個ChannelFutureListener,以便在某個操作完成時(無論成功與否)得到通知。
2.ChannelHandler和ChannelPipeline
2.1 ChannelHandler接口
ChannelHandler的方法是由網絡事件觸發的,它在Netty中充當了處理入站和出站數據的應用程序邏輯的容器。Netty中有許多不同類型的ChannelHandler,它們各自的功能主要取決於它們的超類。Netty以適配器類的形式提供了大量默認的ChannelHandler實現,目的是爲了簡化應用程序處理邏輯的開發過程。
我們知道,ChannelPipeline中的每個ChannelHandler將負責把事件轉發到鏈中的下一個ChannelHandler。這些適配器類將自動執行這個操作,所以我們只需要重寫那些需要特殊處理的方法和事件。
2.2 ChannelPipeline接口
ChannelPipeline爲ChannelHandler鏈提供了容器,並定義了用於該鏈上傳播入站和出站事件流的API。當Channel被創建時,它會被自動地分配到它專屬的ChannelPipeline。ChannelHandler安裝到ChannelPipeline中的過程如下:
- 一個ChannelInitializer的實現被註冊到了ServerBootstrap(或客戶端的BootStrap)
- 當ChannelInitializer.initChannel()方法調用時,ChannelIntializer將在ChannelPipeline中安裝一組自定義的ChannelHandler
- ChannelIntializer將它自己從ChannelPipeline中移除。
ChannelHandler接收事件、執行它們所實現的處理邏輯,並將數據傳遞給鏈中的下一個ChannelHandler。它們的執行順序是由它們被添加的順序所決定的。實際上,被我們成爲ChannelPipeline的是這些ChannelHandler的編排順序。示意圖如下:
當ChannelHandler被添加到ChannelPipeline時,它將會被分配一個ChannelHandlerContext,其代表了ChannelHandler和ChannelPipeline之間的綁定。雖然這個對象可以被用於獲取底層的Channel,但是它主要還是被用於寫出站數據。
在Netty中,有兩種發送消息的方式。你可以直接寫到Channel中,也可以寫到和ChannelHandler相關聯的ChannelHandlerContext對象中。前一種方式將會導致消息從ChannelPipeline的尾端開始流動,而後者將導致消息從ChannelPipeline中的下一個ChannelHandler開始流動。
2.3 編碼器和解碼器
由於網絡數據總是一系列的字節,當你通過Netty發送或者接收一個消息的時候,將會發生一次轉換。入站消息會被解碼,一般情況下是從字節轉換爲一個Java對象。如果是出站消息,則會發生相反方向的轉換,從它的當前格式轉換爲字節。
2.4 抽象類SimpleChannelInboundHandler
我們使用Netty最常見的情況是,創建一個ChannelHandler來接收解碼消息,並對數據進行處理。要創建一個這樣的ChannelHandler,我們只需要擴展基類SimpleChannelInboundHandler<T>,其中T是我們要處理的Java類型。在這個ChannelHandler中,我們需要重寫基類的一個或者多個方法,並且獲取一個到ChannelHandlerContext的引用,這個引用將作爲輸入參數傳遞給ChannelHandler的所有方法。
3.引導
Netty的引導類爲應用程序的網絡層配置提供了容器,這涉及將一個進程綁定到某個指定的端口,或者將一個進程連接到另一個運行在某個指定主機的指定端口上的進程。這兩種類型的引導,一種用於客戶端(BootStrap),一種用於服務端(ServerBootstrap)。
BootStrap和ServerBootStrap有一個明顯的區別:引導一個客戶端只需要一個EventLoopGroup,但是一個ServerBootstrap則需要兩個(也可以是同一個實例)。因爲服務器需要兩組不同的Channel,一組只包含一個ServerChannel,代表服務器自身的已綁定到某個本地端口的正在監聽的套接字。一組將包含所有已創建的用來處理傳入客戶端連接(對於每個服務器已經接受到連接都有一個)的Channel。
4.小結
本章主要重新審視了之前引入的一些概念和組件,特別是ChannelHandler、ChannelPipeline和引導。