http://www.itdecent.cn/p/a618adef427c
Netty實戰(zhàn)入門詳解——讓你徹底記住什么是Netty
Spring Cloud Gateway 管理 Netty 長連接
單機(jī)下Java程序支持百萬長連接參數(shù)調(diào)優(yōu)
Netty 系列之 Netty 百萬級推送服務(wù)設(shè)計要點
1. Netty基本概念
Netty是一個高性能、異步事件驅(qū)動的NIO框架,它提供了對TCP、UDP和文件傳輸?shù)闹С郑鳛橐粋€異步NIO框架,Netty的所有IO操作都是異步非阻塞的,通過Future-Listener機(jī)制,用戶可以方便的主動獲取或者通過通知機(jī)制獲得IO操作結(jié)果。
作為當(dāng)前最流行的NIO框架,Netty在互聯(lián)網(wǎng)領(lǐng)域、大數(shù)據(jù)分布式計算領(lǐng)域、游戲行業(yè)、通信行業(yè)等獲得了廣泛的應(yīng)用,一些業(yè)界著名的開源組件也基于Netty的NIO框架構(gòu)建。如:Dubbo、 RocketMQ、Hadoop的Avro、Spark等。
Netty需要學(xué)習(xí)的內(nèi)容: 編解碼器、TCP粘包/拆包及Netty如何解決、ByteBuf、Channel和Unsafe、ChannelPipeline和ChannelHandler、EventLoop和EventLoopGroup、Future等。
編解碼器:
Java序列化的目的主要有兩個:
網(wǎng)絡(luò)傳輸
對象持久化
Java序列化僅僅是Java編解碼技術(shù)的一種,由于他的種種缺陷,衍生出多種編解碼技術(shù)和框架。
Java序列化的缺點:
無法跨語言
序列化后的碼流太大
序列化性能太低
業(yè)界主流的編解碼框架:
Google Protobuf:支持Java、C++、Python三種語言,高效的編碼性能,結(jié)構(gòu)化數(shù)據(jù)存儲格式(XML,JSON等)
Facebook Thrift:適用于靜態(tài)的數(shù)據(jù)交換,需要先確定好它的數(shù)據(jù)結(jié)構(gòu)。結(jié)構(gòu)變化后需要重新編譯IDL文件,這也是Thrift的弱項。
JBoss Marshalling:是一個Java對象的序列化API,修正了JDK自帶的序列化包的很多問題,但又保持跟java.io.Serializable接口的兼容。
Hessian:一個輕量級的remoting onhttp工具,使用簡單的方法提供了RMI的功能,采用的是二進(jìn)制RPC協(xié)議。
TCP粘包/拆包:
TCP是個“流”協(xié)議,所謂流,就是沒有界限的一串?dāng)?shù)據(jù)。TCP底層并不了解上層業(yè)務(wù)數(shù)據(jù)的具體含義,它會根據(jù)TCP緩存區(qū)的實際情況進(jìn)行包的劃分,所以在業(yè)務(wù)上的一個完整的包,可能被TCP拆分成多個包進(jìn)行發(fā)送,也可能把多個小包封裝成一個大的數(shù)據(jù)包發(fā)送,這就是TCP粘包和拆包問題。
有如下幾種情況:

正常情況

粘包

粘包和拆包同時發(fā)生
2. Netty線程模型
在JAVA NIO方面Selector給Reactor模式提供了基礎(chǔ),Netty結(jié)合Selector和Reactor模式設(shè)計了高效的線程模型。
關(guān)于Java NIO 構(gòu)造Reator模式,Doug Lea在《Scalable IO in Java》中給了很好的闡述,這里截取PPT對Reator模式的實現(xiàn)進(jìn)行說明。
(1)Reactor單線程模型

單線程模型
這是最簡單的Reactor單線程模型,由于Reactor模式使用的是異步非阻塞IO,所有的IO操作都不會被阻塞,理論上一個線程可以獨立處理所有的IO操作。這時Reactor線程是個多面手,負(fù)責(zé)多路分離套接字,Accept新連接,并分發(fā)請求到處理鏈中。
對于一些小容量應(yīng)用場景,可以使用到單線程模型。但對于高負(fù)載,大并發(fā)的應(yīng)用卻不合適,主要原因如下:
1. 當(dāng)一個NIO線程同時處理成百上千的鏈路,性能上無法支撐,即使NIO線程的CPU負(fù)荷達(dá)到100%,也無法完全處理消息。
2. 當(dāng)NIO線程負(fù)載過重后,處理速度會變慢,會導(dǎo)致大量客戶端連接超時,超時之后往往會重發(fā),更加重了NIO線程的負(fù)載。
3. 可靠性低,一個線程意外死循環(huán),會導(dǎo)致整個通信系統(tǒng)不可用。
為了解決這些問題,出現(xiàn)了Reactor多線程模型。
(2)Reactor多線程模型

多線程模型
相比上一種模式,該模型在處理鏈部分采用了多線程(線程池)。
在絕大多數(shù)場景下,該模型都能滿足性能需求。但是,在一些特殊的應(yīng)用場景下,如服務(wù)器會對客戶端的握手消息進(jìn)行安全認(rèn)證。這類場景下,單獨的一個Acceptor線程可能會存在性能不足的問題。為了解決這些問題,產(chǎn)生了第三種Reactor線程模型。
(3)Reactor主從模型?

主從模型
該模型相比第二種模型,是將Reactor分成兩部分,mainReactor負(fù)責(zé)監(jiān)聽server socket,accept新連接;并將建立的socket分派給subReactor。subReactor負(fù)責(zé)多路分離已連接的socket,讀寫網(wǎng)絡(luò)數(shù)據(jù),對業(yè)務(wù)處理功能,其扔給worker線程池完成。通常,subReactor個數(shù)上可與CPU個數(shù)等同。
利用主從NIO線程模型,可以解決一個服務(wù)端監(jiān)聽線程無法有效處理所有客戶端連接的性能不足問題,因此,在Netty的官方Demo中,推薦使用該線程模型。
(4)Netty模型
Netty的線程模型并不是一成不變,它實際取決于用戶的啟動參數(shù)配置。通過設(shè)置不同的啟動參數(shù),Netty可以同時支持Reactor單線程模型、多線程模型和主從模型。
前面介紹完 Netty 相關(guān)一些理論,下面從功能特性、模塊組件來介紹 Netty 的架構(gòu)設(shè)計。
3. Netty功能特性

Netty的功能特性
4. Netty模塊組件
Netty主要有下面一些組件:
Selector
NioEventLoop
NioEventLoopGroup
ChannelHandler
ChannelHandlerContext
ChannelPipeline
Selector
Netty 基于 Selector 對象實現(xiàn) I/O 多路復(fù)用,通過 Selector 一個線程可以監(jiān)聽多個連接的 Channel 事件。
NioEventLoop
其中維護(hù)了一個線程和任務(wù)隊列,支持異步提交執(zhí)行任務(wù),線程啟動時會調(diào)用 NioEventLoop 的 run 方法,執(zhí)行 I/O 任務(wù)和非 I/O 任務(wù)。
NioEventLoopGroup
主要管理 eventLoop 的生命周期,可以理解為一個線程池,內(nèi)部維護(hù)了一組線程,每個線程(NioEventLoop)負(fù)責(zé)處理多個 Channel 上的事件,而一個 Channel 只對應(yīng)于一個線程。
ChannelHandler
是一個接口,處理 I/O 事件或攔截 I/O 操作,并將其轉(zhuǎn)發(fā)到其 ChannelPipeline(業(yè)務(wù)處理鏈)中的下一個處理程序。
ChannelHandlerContext
保存 Channel 相關(guān)的所有上下文信息,同時關(guān)聯(lián)一個 ChannelHandler 對象。
ChannelPipeline 是保存 ChannelHandler 的 List,用于處理或攔截 Channel 的入站事件和出站操作。實現(xiàn)了一種高級形式的攔截過濾器模式,使用戶可以完全控制事件的處理方式,以及 Channel 中各個的 ChannelHandler 如何相互交互。
ChannelPipeline對事件流的攔截和處理流程:

ChannelPipeline入站出站圖
Netty中的事件分為Inbond事件和Outbound事件。
Inbound事件通常由I/O線程觸發(fā),如TCP鏈路建立事件、鏈路關(guān)閉事件、讀事件、異常通知事件等。
Outbound事件通常是用戶主動發(fā)起的網(wǎng)絡(luò)I/O操作,如用戶發(fā)起的連接操作、綁定操作、消息發(fā)送等。
在 Netty中,Channel 、ChannelHandler、ChannelHandlerContext、 ChannelPipeline的關(guān)系如下圖:

各組件關(guān)系圖
一個 Channel 包含了一個 ChannelPipeline,而 ChannelPipeline 中又維護(hù)了一個由 ChannelHandlerContext 組成的雙向鏈表,并且每個 ChannelHandlerContext 中又關(guān)聯(lián)著一個 ChannelHandler。
入站事件和出站事件在一個雙向鏈表中,入站事件會從鏈表 head 往后傳遞到最后一個入站的 handler,出站事件會從鏈表 tail 往前傳遞到最前一個出站的 handler,兩種類型的 handler 互不干擾。
以上是Netty的主要原理介紹,Netty源碼分析的話,后續(xù)有時間會繼續(xù)分享出來。