【轉】目前為止最透徹的的Netty高性能原理和框架架構解析


轉自:https://zhuanlan.zhihu.com/p/48591893

 

1、引言

Netty 是一個廣受歡迎的異步事件驅動的Java開源網絡應用程序框架,用於快速開發可維護的高性能協議服務器和客戶端。

本文基於 Netty 4.1 展開介紹相關理論模型,使用場景,基本組件、整體架構,知其然且知其所以然,希望給大家在實際開發實踐、學習開源項目方面提供參考。

本文作者的另兩篇《高性能網絡編程(五):一文讀懂高性能網絡編程中的I/O模型》、《高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型》也寫的很好,有興趣的讀者可以一並看看。

關於作者:

陳彩華(caison),從事服務端開發,善於系統設計、優化重構、線上問題排查工作,主要開發語言是 Java,微信號:hua1881375。

(本文同步發布於:

2、相關資料

Netty源碼在線閱讀:

Netty-4.1.x地址是:
Netty-4.0.x地址是:
Netty-3.x地址是:

Netty在線API文檔:

Netty-4.1.x API文檔(在線版):
Netty-4.0.x API文檔(在線版):
Netty-3.x API文檔(在線版):

有關Netty的其它精華文章:

有關“為何選擇Netty”的11個疑問及解答
開源NIO框架八卦——到底是先有MINA還是先有Netty?
選Netty還是Mina:深入研究與對比(一)
選Netty還是Mina:深入研究與對比(二)
Netty 4.x學習(一):ByteBuf詳解
Netty 4.x學習(二):Channel和Pipeline詳解
Netty 4.x學習(三):線程模型詳解
實踐總結:Netty3.x升級Netty4.x遇到的那些坑(線程篇)
實踐總結:Netty3.x VS Netty4.x的線程模型
詳解Netty的安全性:原理介紹、代碼演示(上篇)
詳解Netty的安全性:原理介紹、代碼演示(下篇)
詳解Netty的優雅退出機制和原理
NIO框架詳解:Netty的高性能之道
Twitter:如何使用Netty 4來減少JVM的GC開銷(譯文)
絕對干貨:基於Netty實現海量接入的推送服務技術要點
Netty干貨分享:京東京麥的生產級TCP網關技術實踐總結

3、JDK 原生 NIO 程序的問題

JDK 原生也有一套網絡應用程序 API,但是存在一系列問題,主要如下:

1)NIO 的類庫和 API 繁雜,使用麻煩:你需要熟練掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。

2)需要具備其他的額外技能做鋪墊:例如熟悉 Java 多線程編程,因為 NIO 編程涉及到 Reactor 模式,你必須對多線程和網路編程非常熟悉,才能編寫出高質量的 NIO 程序。

3)可靠性能力補齊,開發工作量和難度都非常大:例如客戶端面臨斷連重連、網絡閃斷、半包讀寫、失敗緩存、網絡擁塞和異常碼流的處理等等。NIO 編程的特點是功能開發相對容易,但是可靠性能力補齊工作量和難度都非常大。

4)JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它會導致 Selector 空輪詢,最終導致 CPU 100%。官方聲稱在 JDK 1.6 版本的 update 18 修復了該問題,但是直到 JDK 1.7 版本該問題仍舊存在,只不過該 Bug 發生概率降低了一些而已,它並沒有被根本解決。

4、Netty 的特點

Netty 對 JDK 自帶的 NIO 的 API 進行了封裝,解決了上述問題。

Netty的主要特點有:

1)設計優雅:適用於各種傳輸類型的統一 API 阻塞和非阻塞 Socket;基於靈活且可擴展的事件模型,可以清晰地分離關注點;高度可定制的線程模型 - 單線程,一個或多個線程池;真正的無連接數據報套接字支持(自 3.1 起)。

2)使用方便:詳細記錄的 Javadoc,用戶指南和示例;沒有其他依賴項,JDK 5(Netty 3.x)或 6(Netty 4.x)就足夠了。

3)高性能、吞吐量更高:延遲更低;減少資源消耗;最小化不必要的內存復制。

4)安全:完整的 SSL/TLS 和 StartTLS 支持。

5)社區活躍、不斷更新:社區活躍,版本迭代周期短,發現的 Bug 可以被及時修復,同時,更多的新功能會被加入。

 

7、Netty框架的架構設計

前面介紹完 Netty 相關一些理論,下面從功能特性、模塊組件、運作過程來介紹 Netty 的架構設計。

7.1 功能特性 

Netty 功能特性如下:

1)傳輸服務:支持 BIO 和 NIO;

2)容器集成:支持 OSGI、JBossMC、Spring、Guice 容器;

3)協議支持:HTTP、Protobuf、二進制、文本、WebSocket 等一系列常見協議都支持。還支持通過實行編碼解碼邏輯來實現自定義協議;

4)Core 核心:可擴展事件模型、通用通信 API、支持零拷貝的 ByteBuf 緩沖對象。

7.2 模塊組件

【Bootstrap、ServerBootstrap】:

Bootstrap 意思是引導,一個 Netty 應用通常由一個 Bootstrap 開始,主要作用是配置整個 Netty 程序,串聯各個組件,Netty 中 Bootstrap 類是客戶端程序的啟動引導類,ServerBootstrap 是服務端啟動引導類。

【Future、ChannelFuture】:

正如前面介紹,在 Netty 中所有的 IO 操作都是異步的,不能立刻得知消息是否被正確處理。

但是可以過一會等它執行完成或者直接注冊一個監聽,具體的實現就是通過 Future 和 ChannelFutures,他們可以注冊一個監聽,當操作執行成功或失敗時監聽會自動觸發注冊的監聽事件。

【Channel】:

Netty 網絡通信的組件,能夠用於執行網絡 I/O 操作。Channel 為用戶提供:

1)當前網絡連接的通道的狀態(例如是否打開?是否已連接?)

2)網絡連接的配置參數 (例如接收緩沖區大小)

3)提供異步的網絡 I/O 操作(如建立連接,讀寫,綁定端口),異步調用意味着任何 I/O 調用都將立即返回,並且不保證在調用結束時所請求的 I/O 操作已完成。

4)調用立即返回一個 ChannelFuture 實例,通過注冊監聽器到 ChannelFuture 上,可以 I/O 操作成功、失敗或取消時回調通知調用方。

5)支持關聯 I/O 操作與對應的處理程序。

不同協議、不同的阻塞類型的連接都有不同的 Channel 類型與之對應。

下面是一些常用的 Channel 類型:

NioSocketChannel,異步的客戶端 TCP Socket 連接。
NioServerSocketChannel,異步的服務器端 TCP Socket 連接。
NioDatagramChannel,異步的 UDP 連接。
NioSctpChannel,異步的客戶端 Sctp 連接。
NioSctpServerChannel,異步的 Sctp 服務器端連接,這些通道涵蓋了 UDP 和 TCP 網絡 IO 以及文件 IO。

【Selector】:

Netty 基於 Selector 對象實現 I/O 多路復用,通過 Selector 一個線程可以監聽多個連接的 Channel 事件。

當向一個 Selector 中注冊 Channel 后,Selector 內部的機制就可以自動不斷地查詢(Select) 這些注冊的 Channel 是否有已就緒的 I/O 事件(例如可讀,可寫,網絡連接完成等),這樣程序就可以很簡單地使用一個線程高效地管理多個 Channel 。

【NioEventLoop】:

NioEventLoop 中維護了一個線程和任務隊列,支持異步提交執行任務,線程啟動時會調用 NioEventLoop 的 run 方法,執行 I/O 任務和非 I/O 任務:

I/O 任務,即 selectionKey 中 ready 的事件,如 accept、connect、read、write 等,由 processSelectedKeys 方法觸發。

非 IO 任務,添加到 taskQueue 中的任務,如 register0、bind0 等任務,由 runAllTasks 方法觸發。

兩種任務的執行時間比由變量 ioRatio 控制,默認為 50,則表示允許非 IO 任務執行的時間與 IO 任務的執行時間相等。

【NioEventLoopGroup】:

NioEventLoopGroup,主要管理 eventLoop 的生命周期,可以理解為一個線程池,內部維護了一組線程,每個線程(NioEventLoop)負責處理多個 Channel 上的事件,而一個 Channel 只對應於一個線程。

【ChannelHandler】:

ChannelHandler 是一個接口,處理 I/O 事件或攔截 I/O 操作,並將其轉發到其 ChannelPipeline(業務處理鏈)中的下一個處理程序。

ChannelHandler 本身並沒有提供很多方法,因為這個接口有許多的方法需要實現,方便使用期間,可以繼承它的子類:

ChannelInboundHandler 用於處理入站 I/O 事件。
ChannelOutboundHandler 用於處理出站 I/O 操作。

或者使用以下適配器類:

ChannelInboundHandlerAdapter 用於處理入站 I/O 事件。
ChannelOutboundHandlerAdapter 用於處理出站 I/O 操作。
ChannelDuplexHandler 用於處理入站和出站事件。

【ChannelHandlerContext】:

保存 Channel 相關的所有上下文信息,同時關聯一個 ChannelHandler 對象。

【ChannelPipline】:

保存 ChannelHandler 的 List,用於處理或攔截 Channel 的入站事件和出站操作。

ChannelPipeline 實現了一種高級形式的攔截過濾器模式,使用戶可以完全控制事件的處理方式,以及 Channel 中各個的 ChannelHandler 如何相互交互。

下圖引用 Netty 的 Javadoc 4.1 中 ChannelPipeline 的說明,描述了 ChannelPipeline 中 ChannelHandler 通常如何處理 I/O 事件。

I/O 事件由 ChannelInboundHandler 或 ChannelOutboundHandler 處理,並通過調用 ChannelHandlerContext 中定義的事件傳播方法。

例如:ChannelHandlerContext.fireChannelRead(Object)和 ChannelOutboundInvoker.write(Object)轉發到其最近的處理程序。

 

入站事件由自下而上方向的入站處理程序處理,如圖左側所示。入站 Handler 處理程序通常處理由圖底部的 I/O 線程生成的入站數據。

通常通過實際輸入操作(例如 SocketChannel.read(ByteBuffer))從遠程讀取入站數據。

出站事件由上下方向處理,如圖右側所示。出站 Handler 處理程序通常會生成或轉換出站傳輸,例如 write 請求。

I/O 線程通常執行實際的輸出操作,例如 SocketChannel.write(ByteBuffer)。

在 Netty 中每個 Channel 都有且僅有一個 ChannelPipeline 與之對應,它們的組成關系如下:

 

一個 Channel 包含了一個 ChannelPipeline,而 ChannelPipeline 中又維護了一個由 ChannelHandlerContext 組成的雙向鏈表,並且每個 ChannelHandlerContext 中又關聯着一個 ChannelHandler。

入站事件和出站事件在一個雙向鏈表中,入站事件會從鏈表 head 往后傳遞到最后一個入站的 handler,出站事件會從鏈表 tail 往前傳遞到最前一個出站的 handler,兩種類型的 handler 互不干擾。

9、本文小結

現在推薦使用的主流穩定版本還是 Netty4,Netty5 中使用了 ForkJoinPool,增加了代碼的復雜度,但是對性能的改善卻不明顯,所以這個版本不推薦使用,官網也沒有提供下載鏈接。

Netty 入門門檻相對較高,是因為這方面的資料較少,並不是因為它有多難,大家其實都可以像搞透 Spring 一樣搞透 Netty。

在學習之前,建議先理解透整個框架原理結構,運行過程,可以少走很多彎路。

附錄:更多網絡通信方面的文章

[1] 網絡編程基礎資料:
TCP/IP詳解 -  第11章·UDP:用戶數據報協議
TCP/IP詳解 -  第17章·TCP:傳輸控制協議
TCP/IP詳解 -  第18章·TCP連接的建立與終止
TCP/IP詳解 -  第21章·TCP的超時與重傳
技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)
通俗易懂-深入理解TCP協議(上):理論基礎
通俗易懂-深入理解TCP協議(下):RTT、滑動窗口、擁塞處理
理論經典:TCP協議的3次握手與4次揮手過程詳解
理論聯系實際:Wireshark抓包分析TCP 3次握手、4次揮手過程
計算機網絡通訊協議關系圖(中文珍藏版)
UDP中一個包的大小最大能多大?
P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介
P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解
P2P技術詳解(三):P2P技術之STUN、TURN、ICE詳解
通俗易懂:快速理解P2P技術中的NAT穿透原理
高性能網絡編程(一):單台服務器並發TCP連接數到底可以有多少
高性能網絡編程(二):上一個10年,著名的C10K並發連接問題
高性能網絡編程(三):下一個10年,是時候考慮C10M並發問題了
高性能網絡編程(四):從C10K到C10M高性能網絡應用的理論探索
高性能網絡編程(五):一文讀懂高性能網絡編程中的I/O模型
高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型
不為人知的網絡編程(一):淺析TCP協議中的疑難雜症(上篇)
不為人知的網絡編程(二):淺析TCP協議中的疑難雜症(下篇)
不為人知的網絡編程(三):關閉TCP連接時為什么會TIME_WAIT、CLOSE_WAIT
不為人知的網絡編程(四):深入研究分析TCP的異常關閉
不為人知的網絡編程(五):UDP的連接性和負載均衡
不為人知的網絡編程(六):深入地理解UDP協議並用好它
不為人知的網絡編程(七):如何讓不可靠的UDP變的可靠?
網絡編程懶人入門(一):快速理解網絡通信協議(上篇)
網絡編程懶人入門(二):快速理解網絡通信協議(下篇)
網絡編程懶人入門(三):快速理解TCP協議一篇就夠
網絡編程懶人入門(四):快速理解TCP和UDP的差異
網絡編程懶人入門(五):快速理解為什么說UDP有時比TCP更有優勢
網絡編程懶人入門(六):史上最通俗的集線器、交換機、路由器功能原理入門
網絡編程懶人入門(七):深入淺出,全面理解HTTP協議
網絡編程懶人入門(八):手把手教你寫基於TCP的Socket長連接
技術掃盲:新一代基於UDP的低延時網絡傳輸層協議——QUIC詳解
讓互聯網更快:新一代QUIC協議在騰訊的技術實踐分享
現代移動端網絡短連接的優化手段總結:請求速度、弱網適應、安全保障
聊聊iOS中網絡編程長連接的那些事
移動端IM開發者必讀(一):通俗易懂,理解移動網絡的“弱”和“慢”
移動端IM開發者必讀(二):史上最全移動弱網絡優化方法總結
IPv6技術詳解:基本概念、應用現狀、技術實踐(上篇)
IPv6技術詳解:基本概念、應用現狀、技術實踐(下篇)
從HTTP/0.9到HTTP/2:一文讀懂HTTP協議的歷史演變和設計思路
腦殘式網絡編程入門(一):跟着動畫來學TCP三次握手和四次揮手
腦殘式網絡編程入門(二):我們在讀寫Socket時,究竟在讀寫什么?
腦殘式網絡編程入門(三):HTTP協議必知必會的一些知識
腦殘式網絡編程入門(四):快速理解HTTP/2的服務器推送(Server Push)
腦殘式網絡編程入門(五):每天都在用的Ping命令,它到底是什么?
以網游服務端的網絡接入層設計為例,理解實時通信的技術挑戰
邁向高階:優秀Android程序員必知必會的網絡基礎
>>  更多同類文章 ……
[2] NIO異步網絡編程資料:
Java新一代網絡編程模型AIO原理及Linux系統AIO介紹
NIO框架入門(一):服務端基於Netty4的UDP雙向通信Demo演示
NIO框架入門(二):服務端基於MINA2的UDP雙向通信Demo演示
NIO框架入門(三):iOS與MINA2、Netty4的跨平台UDP雙向通信實戰
NIO框架入門(四):Android與MINA2、Netty4的跨平台UDP雙向通信實戰
Apache Mina框架高級篇(一):IoFilter詳解
Apache Mina框架高級篇(二):IoHandler詳解
MINA2 線程原理總結(含簡單測試實例)
Apache MINA2.0 開發指南(中文版)[附件下載]
MINA、Netty的源代碼(在線閱讀版)已整理發布
解決MINA數據傳輸中TCP的粘包、缺包問題(有源碼)
解決Mina中多個同類型Filter實例共存的問題
>>  更多同類文章 ……

(本文同步發布於:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM