Netty框架學習之(二):Netty組件簡介

1. 概覽

從高層次的角度來看Netty, 它主要爲需要開發高性能應用的開發者解決了“技術”的和“體系結構”的問題。首先,它的基於 Java NIO 的異步的和事件驅動的實現,保證了高負載下應用程序
性能的最大化和可伸縮性。其次, Netty 也包含了一組設計模式,將應用程序邏輯從網絡層解耦,簡化了開發過程,同時也最大限度地提高了可測試性、模塊化以及代碼的可重用性。

爲了可以更好的研究 Netty,本文主要對Netty的組件做一個簡單的描述,以及從高層次的角度來了解各個組件是如何協作的。

Netty中主要組件包括:
- Channel:代表了一個鏈接,與EventLoop一起用來參與IO處理。
- ChannelHandler:爲了支持各種協議和處理數據的方式,便誕生了Handler組件。Handler主要用來處理各種事件,這裏的事件很廣泛,比如可以是連接、數據接收、異常、數據轉換等。
- ChannelPipeline:提供了 ChannelHandler 鏈的容器,並定義了用於在該鏈上傳播入站
和出站事件流的 API。
- EventLoop:Channel處理IO操作,一個EventLoop可以爲多個Channel服務。
- EventLoopGroup:會包含多個EventLoop。

上述組件的關係結構如下圖所示:

這裏寫圖片描述

  1. 一個 EventLoopGroup 包含一個或者多個 EventLoop;
  2. 一個 EventLoop 在它的生命週期內只和一個 Thread 綁定;
  3. 所有由 EventLoop 處理的 I/O 事件都將在它專有的 Thread 上被處理;
  4. 一個 Channel 在它的生命週期內只註冊於一個 EventLoop;
  5. 一個 EventLoop 可能會被分配給一個或多個 Channel。

2. 組件說明

2.1 Channel

在Java中(Socket類),基本的 I/O 操作(bind()、 connect()、 read()和 write())依賴於底層網絡傳輸所提供的功能。 Netty 的 Channel 接
口所提供的 API降低了直接使用 Socket 類的複雜性。Netty中也提供了使用多種方式連接的Channel:
- EmbeddedChannel;
- LocalServerChannel;
- NioDatagramChannel;
- NioSctpChannel;
- NioSocketChannel。

2.2 EventLoop&EventLoopGroup

EventLoop主要用於處理連接的生命週期中所發生的事件,它實現了Netty的線程模型部分的功能,線程模型指定了操作系統/編程語言/框架或者應用程序的上下文中的線程管理的方式。如何以及何時創建線程將對應用程序的性能產生顯著的影響。詳細細節將在後續的博文進行說明,本處只做簡單描述。

Channel&EventLoop&EventLoopGroup的協作方式大致如下:
這裏寫圖片描述

2.3 ChannelHandler&ChannelHandlerPipline

ChannelHandler

對於開發一個Netty的應用而言,主要開發的組件可能就是 ChannelHandler, 它充當了所有處理入站和出站數據的應用程序邏輯的容器,根據數據流向的不同,ChannelHandler可以分爲ChannelInboundHandler以及ChannelOutboundHandler.

ChannelPipeline

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

ChannelHandler 安裝到 ChannelPipeline 中的過程如下所示:
1. 一個ChannelInitializer被註冊到了ServerBootstrap中;
2. 當 ChannelInitializer.initChannel()方法被調用時, ChannelInitializer
將在 ChannelPipeline 中安裝一組自定義的 ChannelHandler;
3. ChannelInitializer 將它自己從 ChannelPipeline 中移除

事件進入ChannelPipline時,會被定義的ChannelHandler順序的進行處理,如下圖所示:

這裏寫圖片描述

從一個客戶端應用程序的角度來看,如果事件的運動方向是從客戶端到服務器端,那麼我們稱這些事件爲Outbound的,反之則稱爲Inbound的。需要注意的是對於inbound操作而言,處理的順序爲從頭到尾,outbound的處理順序爲從尾到頭。上圖中Inbound的處理順序爲(1,2).Outbound的處理順序爲(3,4).

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

==在 Netty 中,有兩種發送消息的方式。你可以直接寫到 Channel 中,也可以 寫到和 ChannelHandler相關聯的ChannelHandlerContext對象中。前一種方式將會導致消息從ChannelPipeline 的尾端開始流動,而後者將導致消息從 ChannelPipeline 中的下一個 ChannelHandler 開始流動。==

ChannelHandler的類結構

首先要說明的是ChannelInboundHandle 和ChannelOutboundHandle 都繼承自ChannelHandler,將兩個類別的 ChannelHandler都混合添加到同一個 ChannelPipeline 中時,Netty 能區分 ChannelInboundHandler 實現和 ChannelOutboundHandler 實現,並確保數據只會在具有相同定向類型的兩個 ChannelHandler 之間傳遞。

對於ChannelHandler的實現類而言,可能不需要關注事件處理週期的每個環節,如果要把Inbound或是Outboud接口的每個方法都實現,就會額外的帶來很多的工作量,Netty對於該種情況提供了幾種Adapter的解決方案:

  • ChannelHandlerAdapter
  • ChannelInboundHandlerAdapter
  • ChannelOutboundHandlerAdapter
  • ChannelDuplexHandler

2.4 編碼器&解碼器

當通過 Netty 發送或者接收一個消息的時候,就將會發生一次數據轉換。入站消息會被解碼:從字節轉換爲另一種格式,通常是一個 Java 對象。如果是出站消息,則會發生
編碼:將從它的當前格式被編碼爲字節。

Netty默認已經提供了一堆的編/解碼器,一般需求已經可以滿足,如果不滿足可以通繼承ByteToMessageDecoder 或 MessageToByteEncoder來實現自己的編/解碼器。通過查看類的繼承結構可以看出 Netty 提供的編碼器/解碼器適配器類都實現了 ChannelOutboundHandler 或者 ChannelInboundHandler 接口。

對於解碼器Handler而言,其重寫了channelRead方法,對於每個從入站
Channel 讀取的消息,這個方法都將會被調用。隨後,它將調用由解碼器所提供的 decode()方法,並將已解碼的字節轉發給 ChannelPipeline 中的下一個 ChannelInboundHandler。OutboundHandler採用相反的處理方式。

2.5 SimpleChannelInboundHandler

你的應用程序會利用一個 ChannelHandler 來接收解碼消息,並對該數據應用業務邏輯。 這種情況下,你只需要擴展基類 SimpleChannelInboundHandler,其中 T 是你要處理的消息的 Java 類型。

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

2.6 引導程序

Netty 的引導類爲應用程序的網絡層配置提供了容器,這涉及將一個進程綁定到某個指定的端口,或者將一個進程連接到另一個運行在某個指定主機的指定端口上的進程。

有兩種類型的引導:
1. 用於客戶端(Bootstrap)
2. 用於服務器(ServerBootstrap)。

需要注意的是,引導一個客戶端只需要一個 EventLoopGroup,但是一個ServerBootstrap 則需要兩個(也可以是同一個實例)

下圖說明了Server端兩個EventLoopGroup的用途:

這裏寫圖片描述

第一個EventLoopGroup用來專門負責綁定到端口監聽連接事件,而把第二個EventLoopGroup用來處理每個接收到的連接。

對於Server端,如果僅由一個EventLoopGroup處理所有請求和連接的話,在併發量很大的情況下,這個EventLoopGroup有可能會忙於處理已經接收到的連接而不能及時處理新的連接請求,用兩個的話,會有專門的線程來處理連接請求,不會導致請求超時的情況,大大提高了併發處理能力

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章