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

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

 

一、引言

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

本文基於 Netty 4.1 展開介紹相關理論模型,使用場景,基本組件、總體架構,知其然且知其因此然,但願給你們在實際開發實踐、學習開源項目方面提供參考。程序員

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

關於做者:api

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

(本文同步發佈於:http://www.52im.net/thread-2043-1-1.html緩存

二、相關資料

Netty源碼在線閱讀:安全

Netty-4.1.x地址是: http://docs.52im.net/extend/docs/src/netty4_1/
Netty-4.0.x地址是: http://docs.52im.net/extend/docs/src/netty4/
Netty-3.x地址是: http://docs.52im.net/extend/docs/src/netty3/

Netty在線API文檔:服務器

Netty-4.1.x API文檔(在線版): http://docs.52im.net/extend/docs/api/netty4_1/
Netty-4.0.x API文檔(在線版): http://docs.52im.net/extend/docs/api/netty4/
Netty-3.x API文檔(在線版): http://docs.52im.net/extend/docs/api/netty3/

有關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網關技術實踐總結

三、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 發生機率下降了一些而已,它並無被根本解決。

四、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 能夠被及時修復,同時,更多的新功能會被加入。

 

七、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 互不干擾。

九、本文小結

如今推薦使用的主流穩定版本仍是 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與MINA二、Netty4的跨平臺UDP雙向通訊實戰
NIO框架入門(四):Android與MINA二、Netty4的跨平臺UDP雙向通訊實戰
Apache Mina框架高級篇(一):IoFilter詳解
Apache Mina框架高級篇(二):IoHandler詳解
MINA2 線程原理總結(含簡單測試實例)
Apache MINA2.0 開發指南(中文版)[附件下載]
MINA、Netty的源代碼(在線閱讀版)已整理髮布
解決MINA數據傳輸中TCP的粘包、缺包問題(有源碼)
解決Mina中多個同類型Filter實例共存的問題
>>  更多同類文章 ……

(本文同步發佈於:http://www.52im.net/thread-2043-1-1.html

相關文章
相關標籤/搜索