Netty組件概覽

Channel、EventLoop和ChannelFuture

  1. Channel——Socket;
  2. EventLoop——控制流、多線程處理、并發
  3. ChannelFuture異步通知

Channel接口

基于I/O操作(例如:bind()、connect()、read()和write())依賴于底層網絡傳輸提供的原語。在基于Java的網絡編程中,其基本構造為類Socket。

Netty的Channel接口所提供的API,大大降低了直接使用Socket類的復雜性。

Channel擁有許多預定義的、專門化實現的廣泛類層次結構的根,如下:

  1. EmbeddedChannel
  2. LocalServerChannel
  3. NioDatagramChannel
  4. NioSctpChannel
  5. NioSocketChannel

EventLoop接口

EventLoop定義了Netty的核心抽象,用于處理連接的生命周期中所發生的事件。

<center>Channel、EventLoop、Thread、EventLoopGroup關系示意圖</center>

關系圖
  • 一個EventLoopGroup包含一個或者多個EventLoop
  • 一個EventLoop在它的生命周期內只和一個Thread綁定
  • 所有由EventLoop處理的I/O事件都將在它專有的Thread上被處理
  • 一個Channel在它的生命周期內只注冊于一個EventLoop
  • 一個EventLoop可能會被分配一個或多個Channel

一個給定Channel的I/O操作都是由相同的Thread執行的,實際上消除了對于同步的需要

ChanneFuture接口

Netty中所有的I/O操作都是異步的,因為一個操作不可能立即返回,所以我們需要一種用于在之后的某個時間點確定其結果的方法。

因此Netty提供了ChannelFuture接口,其addListener()方法注冊了一個ChannelFutureListener,以便在某個操作完成時(無論是否成功)得到通知。

ChannelHandler和ChannelPipeline

主要用來管理數據流已經執行應用程序處理邏輯

ChannelHandler接口

ChannelHandler充當了所有處理入站和出站數據的應用程序邏輯的容器。

ChannelHandler可專門用于幾乎任何類型的動作,例如將數據從一種格式轉換為另外一種格式,或者處理轉換過程中所拋出的異常。

ChannelHandler可以用來接收入站事件和數據,隨后使用應用程序的業務邏輯進行處理。當你的客戶端需要發送響應時,可以從ChannelInboundhandler沖刷數據。

你的應用程序的業務邏輯通常駐留在一個或者多個ChannelInboundHandler中。

ChannelPipeline

ChannelPipeline是ChannelHandler鏈的容器,并定義用于在該鏈上傳播入站和出站事件流的API。當Channel被創建時,它會被自動地分配到它專屬的ChannelPipeline。

ChannelHandler被安裝到ChannelPipeline中過程如下:

  1. 一個ChannelInitializer的實現被注冊到了ServerBootstrap中
  2. 當ChannelInitializer.initChannel()方法被調用時,ChannelInitalizer將在ChannelPipeline中安裝一組自定義的ChannelHandler
  3. ChannelInitializer將它自己從ChannelPipeline中移除

?ChannelHandler可以讓事件流經ChannelPipeline,它們是在應用程序的初始化或者引導階段被安裝的。這些對象接收事件、執行它們所實現的處理邏輯,并將數據傳遞給鏈中的下一個ChannelHandler。它們的執行順序是由它們被添加的順序決定的。

ChannelPipeline是這些ChannelHandler的編排順序。

入站和出站ChannelHandler可以被安裝到同一個ChannelPipeline中。如果一個消息或者任何其他的入站事件被讀取,那么它會從ChannelPipeline的頭部開始流動,并被傳遞給第一個ChannelInboundHandler。這個ChannelHandler不一定會實際地修改數據,具體取決于它的具體功能,在這之后,數據將會被傳遞給鏈中的下一個ChannelInboundHandler。最終,數據會到達ChannelPipeline的尾端,屆時,所有處理就都結束了。

出站數據將會從ChannelOutboundHandler鏈的尾端開始流動,直到它到達鏈的頭部為止。在這之后,出站數據將會到達網絡傳輸層。

通過使用作為參數傳遞到每個方法的ChannelHandlerContext,事件可以被傳遞給當前ChannelPipeLine中的下一個ChannelHandler。因為用戶并不是關心所有的事件,因此Netty提供了抽象類ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter。通過調用ChannelHandlerContext上的對應的方法,都可以簡單地將事件傳遞給下一個ChannelHandler的方法的實現。

當ChannelHandler被添加到ChannelPipeline時,它將會被分配一個ChannelHandlerContext,它代表了Channel和ChannelPipeline之間的綁定。雖然這個對象可以被用于獲取底層的Channel,但是它主要還是被用于出站寫數據。

在Netty中,有兩種發送消息的方式:

  1. 直接寫到Channel中,會導致消息從ChannelPipeline的尾端開始流動
  2. 寫到和ChannelHandler相關聯的ChannelHandlerContext對象中,會導致消息從ChannelPipeline中的下一個ChannelHandler開始流動

如果將兩個類別(Inboud和Outbound)的ChannelHandler都混合添加到一個ChannelPipeline會發生什么?

雖然ChannelInboundHandler和ChannelOutboundHandler都擴展自ChannelHandler,但是Netty可以區分兩種Handler的實現,并確保數據只會在具有相同定向類型的兩個ChannelHandler之間傳遞。

深入了解ChannelHandler

不同類型的ChannelHandler各自的功能主要取決于它們的超類。Netty以適配器類的形式提供了大量默認的ChannelHandler實現,其旨在簡化應用程序處理邏輯的開發過程。

這些適配器類可以自動將事件轉發到ChannelPipeline中的下一個ChannelHandler,所以你可以只重寫那些你想要特殊處理的方法和事件。

為什么需要適配器?

有一些適配器類可以將編寫自定義的ChannelHanlder所需要的努力降到最低限度,因為它們提供了定義在對應接口中的所有方法的默認實現。
常用的適配器類有:

  • ChannelHandlerAdapter
  • ChannelInboundHandlerAdapter
  • ChannelOutboundHandlerAdapter
  • ChannelDuplexHandler

編碼解碼器

所有由Netty提供的編碼/解碼適配器類都實現了ChannelOutboundHandler或者ChannelInboundHandler接口。

對于入站數據,channelRead方法已經被重寫了。對于每一個從入站Channel讀取的消息,這個方法都會被調用。隨后,它將調用由預置解碼器提供的decode()方法,并將已解碼的字節轉發給ChannelPipeline中的下一個ChannelHandler,出站相反。

抽象類SimpleChannelInboundHandler

最常見的一個情況,你的應用程序會利用一個ChannelHandler來接受解碼消息,并對該數據應用業務邏輯。要創建一個這樣的ChannelHandler,只需要擴展SimpleChannelInboundHandler<T>,其中<T>是你要處理的消息的Java類型。

在這個ChannelHandler中,你將需要重寫基類的一個或者多個方法,并且獲取一個ChannelHandlerContext的引用,這個引用將作為參數傳遞給ChannelHandler的所有方法。

在這種類型的ChannelHandler中,最重要的方法是channelRead0(ChannelHandlerContext , T)。除了要求不阻塞當前的I/O線程之外,其具體實現完全取決于你。

引導

Netty的引導類為應用程序的網絡層配置提供了容器。

  1. 用于客戶端(Bootstrap)引導,將一個進程連接到另一個運行在某個指定主機的指定端口上的進程。
  2. 用于服務器(ServerBootstrap)引導,將一個進程綁定到某個指定的端口

<center>Bootstrap類比較</center>

類別 Bootstrap ServerBootstrap
網絡編程中的作用 連接到遠程主機和端口 綁定到一個本地端口
EventLoopGroup的數目 1 2

區別分析:

  1. ServerBootstrap將綁定到一個端口,因為服務器必須要監聽連接,而Bootstrap則是由想要連接到遠程節點的客戶端應用程序所使用的。
  2. 為什么服務端需要兩個EventLoopGroup(可以是同一個實例)?因為服務器需要兩組不同的Channel。第一組將只包含一個ServerChannel,代表服務器自身的已綁定到某個本地端口第二次正在監聽的套接字。第二組包含所有已經創建的用來處理傳入客戶端連接(對于每個服務器已經接受的連接都一個)的Channel。
    3.與ServerChannel相關聯的EventLoopGroup將分配一個負責為傳入連接請求創建Channel的EventLoop。一旦連接被接受,第二個EventLoopGroup就會給它的Channel分配一個EventLoop。

<center>具有兩個EventLoopGroup的服務器</center>

EventLoopGroup服務器
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容