1. 程式人生 > >38、對比Java標準NIO類庫,你知道Netty是如何實現更高效能的嗎?

38、對比Java標準NIO類庫,你知道Netty是如何實現更高效能的嗎?

今天我會對 NIO 進行一些補充,在專欄第 11 講中,我們初步接觸了 Java 提供的幾種 IO 機制,作為語言基礎類庫,Java 自身的 NIO 設計更偏底層,這本無可厚非,但是對於一線的應用開發者,其複雜性、擴充套件性等方面,就存在一定的侷限了。在基礎 NIO 之上,Netty 構建了更加易用、高效能的網路框架,廣泛應用於網際網路、遊戲、電信等各種領域。

今天我要問你的問題是,對比 Java 標準 NIO 類庫,你知道 Netty 是如何實現更高效能的嗎?

典型回答

單獨從效能角度,Netty 在基礎的 NIO 等類庫之上進行了很多改進,例如:

  •   更加優雅的 Reactor 模式實現、靈活的執行緒模型、利用 EventLoop 等創新性的機制,可以非常高效地管理成百上千的 Channel。
  •   充分利用了 Java 的 Zero-Copy 機制,並且從多種角度,“斤斤計較”般的降低記憶體分配和回收的開銷。例如,使用池化的 Direct Buffer 等技術,在提高 IO 效能的同時,減少了物件的建立和銷燬;利用反射等技術直接操縱 SelectionKey,使用陣列而不是 Java 容器等。
  •   使用更多原生代碼。例如,直接利用 JNI 呼叫 Open SSL 等方式,獲得比 Java 內建 SSL 引擎更好的效能。
  •   在通訊協議、序列化等其他角度的優化。

 
總的來說,Netty 並沒有 Java 核心類庫那些強烈的通用性、跨平臺等各種負擔,針對性能等特定目標以及 Linux 等特定環境,採取了一些極致的優化手段。

 

考點分析

這是一個比較開放的問題,我給出的回答是個概要性的舉例說明。面試官很可能利用這種開放問題作為引子,針對你回答的一個或者多個點,深入探討你在不同層次上的理解程度。

在面試準備中,兼顧整體性的同時,不要忘記選定個別重點進行深入理解掌握,最好是進行原始碼層面的深入閱讀和實驗。如果你希望瞭解更多從效能角度 Netty 在編碼層面的手段,可以參考 Norman 在 Devoxx 上的分享,其中的很多技巧對於實現極致效能的 API 有一定借鑑意義,但在一般的業務開發中要謹慎採用。

雖然提到 Netty,人們會自然地想到高效能,但是 Netty 本身的優勢不僅僅只有這一個方面,

下面我會側重兩個方面:

  •   對 Netty 進行整體介紹,幫你瞭解其基本組成。
  •   從一個簡單的例子開始,對比在第 11 講中基於 IO、NIO 等標準 API 的例項,分析它的技術要點,給你提供一個進一步深入學習的思路。


知識擴充套件

首先,我們從整體瞭解一下 Netty。按照官方定義,它是一個非同步的、基於事件 Client/Server 的網路框架,目標是提供一種簡單、快速構建網路應用的方式,同時保證高吞吐量、低延時、高可靠性。

從設計思路和目的上,Netty 與 Java 自身的 NIO 框架相比有哪些不同呢?

我們知道 Java 的標準類庫,由於其基礎性、通用性的定位,往往過於關注技術模型上的抽象,而不是從一線應用開發者的角度去思考。我曾提到過,引入併發包的一個重要原因就是,應用開發者使用例Thread API 比較痛苦,需要操心的不僅僅是業務邏輯,而且還要自己負責將其對映到 Thread 模型上。

Java NIO 的設計也有類似的特點,開發者需要深入掌握執行緒、IO、網路等相關概念,學習路徑很長,很容易導致程式碼複雜、晦澀,即使是有經驗的工程師,也難以快速地寫出高可靠性的實現。

Netty 的設計強調了 “Separation Of Concerns”,通過精巧設計的事件機制,將業務邏輯和無關技術邏輯進行隔離,並通過各種方便的抽象,一定程度上填補了了基礎平臺和業務開發之間的鴻溝,更有利於在應用開發中普及業界的最佳實踐。

另外,Netty > java.nio + java. net!

從 API 能力範圍來看,Netty 完全是 Java NIO 框架的一個大大的超集,你可以參考 Netty 官方的模組劃分。

除了核心的事件機制等,Netty 還額外提供了很多功能,例如:

  •   從網路協議的角度,Netty 除了支援傳輸層的 UDP、TCP、SCTP協議,也支援 HTTP(s)、WebSocket 等多種應用層協議,它並不是單一協議的 API。
  •   在應用中,需要將資料從 Java 物件轉換成為各種應用協議的資料格式,或者進行反向的轉換,Netty 為此提供了一系列擴充套件的編解碼框架,與應用開發場景無縫銜接,並且效能良好。
  •   它擴充套件了 Java NIO Buffer,提供了自己的 ByteBuf 實現,並且深度支援 Direct Buffer 等技術,甚至 hack 了 Java  內部對 Direct Buffer 的分配和銷燬等。同時,Netty 也提供了更加完善的 Scatter/Gather 機制實現。


可以看到,Netty 的能力範圍大大超過了 Java 核心類庫中的 NIO 等 API,可以說它是一個從應用視角出發的產物。

當然,對於基礎 API 設計,Netty 也有自己獨到的見解,未來 Java NIO API 也可能據此進行一定的改進,如果你有興趣可以參考JDK-8187540。

接下來,我們一起來看一個入門的程式碼例項,看看 Netty 應用到底是什麼樣子。

與第 11 講類似,同樣是以簡化的 Echo Server 為例,下圖是 Netty 官方提供的 Server 部分,完整用例請點選連結。

上面的例子,雖然程式碼很短,但已經足夠體現出 Netty 的幾個核心概念,請注意我用紅框標記出的部分:

  •   ServerBootstrap,伺服器端程式的入口,這是 Netty 為簡化網路程式配置和關閉等生命週期管理,所引入 Bootstrapping  機制。我們通常要做的建立 Channel、繫結埠、註冊 Handler 等,都可以通過這個統一的入口,以Fluent API 等形式完成,相對簡化了 API 使用。與之相對應, Bootstrap則是 Client 端的通常入口。
  •   Channel,作為一個基於 NIO 的擴充套件框架,Channel 和 Selector 等概念仍然是 Netty 的基礎元件,但是針對應用開發具體需求,提供了相對易用的抽象。
  •   EventLoop,這是 Netty 處理事件的核心機制。例子中使用了 EventLoopGroup。我們在 NIO 中通常要做的幾件事情,如註冊感興趣的事件、排程相應的 Handler 等,都是 EventLoop 負責。
  •   ChannelFuture,這是 Netty 實現非同步 IO 的基礎之一,保證了同一個 Channel 操作的呼叫順序。Netty 擴充套件了 Java 標準的 Future,提供了針對自己場景的特有Future定義。
  •   ChannelHandler,這是應用開發者放置業務邏輯的主要地方,也是我上面提到的“Separation Of Concerns”原則的體現。ChannelPipeline,它是 ChannelHandler 鏈條的容器,每個 Channel 在建立後,自動被分配一個 ChannelPipeline。在上面的示例中,我們通過 ServerBootstrap 註冊了 ChannelInitializer,並且實現了  initChannel 方法,而在該方法中則承擔了向 ChannelPipleline 安裝其他 Handler 的任務。


你可以參考下面的簡化示意圖,忽略 Inbound/OutBound Handler 的細節,理解這幾個基本單元之間的操作流程和對應關係。

對比 Java 標準 NIO 的程式碼,Netty 提供的相對高層次的封裝,減少了對 Selector 等細節的操縱,而 EventLoop、Pipeline 等機制則簡化了程式設計模型,開發者不用擔心併發等問題,在一定程度上簡化了應用程式碼的開發。最難能可貴的是,這一切並沒有以可靠性、可擴充套件性為代價,反而將其大幅度提高。

我在專欄週末福利中已經推薦了 Norman Maurer 等編寫的《Netty 實戰》(Netty In Action),如果你想系統學習 
Netty,它會是個很好的入門參考。針對 Netty 的一些實現原理,很可能成為面試中的考點,例如:

  •   Reactor 模式和 Netty 執行緒模型。
  •   Pipelining、EventLoop 等部分的設計實現細節。
  •   Netty 的記憶體管理機制、引用計數等特別手段。
  •   有的時候面試官也喜歡對比 Java 標準 NIO API,例如,你是否知道 Java NIO 早期版本中的 Epoll空轉問題,以及 Netty 的解決方式等。


對於這些知識點,公開的深入解讀已經有很多了,在學習時希望你不要一開始就被複雜的細節弄暈,可以結合例項,逐步、有針對性的進行學習。我的一個建議是,可以試著畫出相應的示意圖,非常有助於理解並能清晰闡述自己的看法。

今天,從 Netty 效能的問題開始,我概要地介紹了 Netty 框架,並且以 Echo Server 為例,對比了 Netty 和 Java NIO 在設計上的不同。但這些都僅僅是冰山的一角,全面掌握還需要下非常多的功夫。

 

一課一練

關於今天我們討論的題目你做到心中有數了嗎?今天的思考題是,Netty 的執行緒模型是什麼樣的?