1. 程式人生 > >Netty框架學習之(三):細說Netty的資料傳輸

Netty框架學習之(三):細說Netty的資料傳輸

1. 概述

使用Java 自帶的API開發IO系統時,如果需要對傳輸的方式進行切換,例如從阻塞傳輸切換到非阻塞傳輸, 那麼可能會由於兩種方式的API不相容問題需要大面積的修改程式碼。然而 Netty 則為它所有的傳輸方式提供了一個通用 API,這使得只需要修改一下物件申明的型別就能完成傳輸方式的切換,例如從OIO切換到NIO只需要如下操作:

OIO的程式碼:
EventLoopGroup group = new OioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.
group(group) .channel(OioServerSocketChannel.class) ......... } finally { group.shutdownGracefully().sync(); } NIO的程式碼: EventLoopGroup group = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.
group(group) .channel(NioServerSocketChannel.class) ......... } finally { group.shutdownGracefully().sync(); }

是不是很方便?

2. API說明

傳輸API的核心Channel,它被用於所有的IO操作,Channel的類圖如下所示:

這裡寫圖片描述

如圖所示,每個 Channel 都將會被分配一個 ChannelPipeline 和 ChannelConfig。
ChannelConfig 包含了該 Channel 的所有配置設定,並且支援熱更新.

由於 Channel 是獨一無二的,所以為了保證順序將 Channel 宣告為 java.lang.
Comparable 的一個子介面。因此,如果兩個不同的 Channel 例項都返回了相同的雜湊碼,那麼 AbstractChannel 中的 compareTo()方法的實現將會丟擲一個 Error.

ChannelPipeline 持有所有將應用於入站和出站資料以及事件的 ChannelHandler 實
例,這些 ChannelHandler 實現了應用程式用於處理狀態變化以及資料處理的邏輯,例如:

  • 將資料從一種格式轉換為另一種格式;
  • 提供異常的通知;
  • 提供 Channel 變為活動的或者非活動的通知;
  • 提供當 Channel 註冊到 EventLoop 或者從 EventLoop 登出時的通知;
  • 提供有關使用者自定義事件的通知。

也可以根據需要通過新增或者移除ChannelHandler例項來修改ChannelPipeline,通過利用Netty的這項能力可以構建出高度靈活的應用程式。

3. 內建的傳輸方式

Netty內建了多種型別的傳輸方式,不同的傳輸方式有不同的應用場景與支援的協議,目前常見的型別包括以下幾種

3.1 NIO

NIO型別的傳輸提供了一個所有 I/O 操作的全非同步的實現,主要利用了自JAVA 自帶 NIO 子系統的選擇器API。

3.2 Epoll

Netty 的 NIO 傳輸基於 Java 提供的非同步/非阻塞網路程式設計的通用抽象。
雖然這保證了 Netty 的非阻塞 API 可以在任何平臺上使用,但它也包含了相應的限制,因為 JDK
為了在所有系統上提供相同的功能,必須做出妥協。

Epoll自Linux核心版本 2.5.44(2002)被引入,提供了比舊的POSIX select和poll系統呼叫更好的效能。如果你的應用程式旨在運行於Linux系統,那麼可以考慮利用這個版本的傳輸;你將發現在高負載下它的效能要優於JDK的NIO實現

3.3 OIO

Netty 的 OIO 傳輸實現代表了一種折中:它可以通過常規的傳輸 API 使用,但是由於它
是建立在 java.net 包的阻塞實現之上的,所以它不是非同步的。但是,它仍然非常適合於某些用途。例如,你可能需要移植使用了一些進行阻塞呼叫的庫(如JDBC)的遺留程式碼,而將邏輯轉換為非阻塞的可能也是不切實際的。相反,你可以在短期內使用Netty的OIO傳輸,然後再將你的程式碼移植到純粹的非同步傳輸上。

Netty是如何能夠使用和用於非同步傳輸相同的API來支援OIO的呢?
答案就是, Netty利用了SO_TIMEOUT這個Socket標誌,它指定了等待一個I/O操作完成的最大毫秒數。如果操作在指定的時間間隔內沒有完成, 則將會丟擲一個SocketTimeout Exception。 Netty將捕獲這個異常並繼續處理迴圈。在EventLoop下一次執行時,它將再次嘗試。這實際上也是類似於Netty這樣的非同步框架能夠支援OIO的唯一方式。

3.4 Local

Netty 提供了一個 Local 傳輸,用於在同一個 JVM 中執行的客戶端和伺服器程式之間的非同步通訊。

在同一個 JVM 內部的通訊,不需要通過網路暴露服務,是
Local 傳輸的完美用例。這將消除所有真實網路操作的開銷,同時仍然使用你的 Netty 程式碼庫。如果隨後需要通過網路暴露服務,那麼你將只需要把傳輸改為 NIO 或者 OIO 即可。

3.5 Embedded

如果你想要為自己的 ChannelHandler 實現編
寫單元測試,可以考慮使用 Embedded 傳輸。這既便於測試你的程式碼,而又不需要建立大
量的模擬(mock)物件。你的類將仍然符合常規的 API 事件流, 保證該 ChannelHandler
在和真實的傳輸一起使用時能夠正確地工作。