Netty高性能架構(gòu)的理解之道

Netty的簡單介紹

Netty 是一個 NIO client-server(客戶端服務(wù)器)框架,使用 Netty 可以快速開發(fā)網(wǎng)絡(luò)應(yīng)用,例如服務(wù)器和客戶 端協(xié)議。 Netty 提供了一種新的方式來使開發(fā)網(wǎng)絡(luò)應(yīng)用程序,這種新的方式使得它很容易使用和有很強(qiáng)的擴(kuò)展性。 Netty 的內(nèi)部實(shí)現(xiàn)時(shí)很復(fù)雜的,但是 Netty 提供了簡單易用的 api 從網(wǎng)絡(luò)處理代碼中解耦業(yè)務(wù)邏輯。 Netty 是完全基 于 NIO 實(shí)現(xiàn)的,所以整個 Netty 都是異步的。

簡單點(diǎn)說就是Netty提供了一個簡單,間接的方法來操作網(wǎng)絡(luò)之間的通訊。

使用 Netty 能夠做什么?

?開發(fā)異步、非阻塞的 TCP 網(wǎng)絡(luò)應(yīng)用程序;

?開發(fā)異步、非阻塞的 UDP 網(wǎng)絡(luò)應(yīng)用程序;

?開發(fā)異步文件傳輸應(yīng)用程序;

開發(fā)異步 HTTP 服務(wù)端和客戶端應(yīng)用程序;

提供對多種編解碼框架的集成,包括谷歌的 Protobuf、Jbossmarshalling、Java 序列化、壓縮編解碼、XML 解碼、字符串編解碼等,這些編解碼框架可以被用戶直接使用;

提供形式多樣的編解碼基礎(chǔ)類庫,可以非常方便的實(shí)現(xiàn)私有協(xié)議棧編解碼框架的二次定制和開發(fā);

基于職責(zé)鏈模式的 Pipeline-Handler 機(jī)制,用戶可以非常方便的對網(wǎng)絡(luò)事件進(jìn)行攔截和定制;

所有的 IO 操作都是異步的,用戶可以通過 Future-Listener 機(jī)制主動 Get 結(jié)果或者由 IO 線程操作完成之后主動 Notify 結(jié)果,用戶的業(yè)務(wù)線程不需要同步等待;

IP 黑白名單控制;

打印消息碼流;

流量控制和整形;

性能統(tǒng)計(jì);

基于鏈路空閑事件檢測的心跳檢測

? ……

Netty 在哪些行業(yè)得到了應(yīng)用??

互聯(lián)網(wǎng)行業(yè):隨著網(wǎng)站規(guī)模的不斷擴(kuò)大,系統(tǒng)并發(fā)訪問量也越來越高,傳統(tǒng)基于 Tomcat 等 Web 容器的垂直架構(gòu)已經(jīng)無法滿足需求,需要拆分應(yīng)用進(jìn)行服務(wù)化,以提高開發(fā)和維護(hù)效率。從組網(wǎng)情況看,垂直的架構(gòu)拆分之后,系統(tǒng)采用分布式部署,各個節(jié)點(diǎn)之間 需要遠(yuǎn)程服務(wù)調(diào)用,高性能的 RPC 框架必不可少,Netty 作為異步高性能的通信框架,往往作為基礎(chǔ)通信組件被這些 RPC 框架使用。

典型的應(yīng)用有:阿里分布式服務(wù)框架 Dubbo 的 RPC 框架使用 Dubbo 協(xié)議進(jìn)行節(jié)點(diǎn)間通信,Dubbo 協(xié)議默認(rèn)使用 Netty 作為基礎(chǔ)通信組件,用于實(shí)現(xiàn)各進(jìn)程節(jié)點(diǎn)之間的內(nèi)部通信。它的架構(gòu)圖如下:

圖、Dubbo 節(jié)點(diǎn)間調(diào)用關(guān)系圖

其中,服務(wù)提供者和服務(wù)消費(fèi)者之間,服務(wù)提供者、服務(wù)消費(fèi)者和性能統(tǒng)計(jì)節(jié)點(diǎn)之間使用 Netty 進(jìn)行異步/同步通信。

除了 Dubbo 之外,淘寶的消息中間件 RocketMQ 的消息生產(chǎn)者和消息消費(fèi)者之間,也采用 Netty 進(jìn)行高性能、異步通信。

除了阿里系和淘寶系之外,很多其它的大型互聯(lián)網(wǎng)公司或者電商內(nèi)部也已經(jīng)大量使用 Netty 構(gòu)建高性能、分布式的網(wǎng)絡(luò)服務(wù)器。

游戲行業(yè):無論是手游服務(wù)端、還是大型的網(wǎng)絡(luò)游戲,Java 語言得到了越來越廣泛的應(yīng)用。Netty 作為高性能的基礎(chǔ)通信組件,它本身提供了 TCP/UDP 和 HTTP 協(xié)議棧,非常方便定制和開發(fā)私有協(xié)議棧。賬號登陸服務(wù)器、地圖服務(wù)器之間可以方便的通過 Netty 進(jìn)行高性能的通信,架構(gòu)示意圖如下:

圖、Netty 在游戲服務(wù)器架構(gòu)中的應(yīng)用

大數(shù)據(jù)領(lǐng)域:經(jīng)典的 Hadoop 的高性能通信和序列化組件 Avro 的 RPC 框架,默認(rèn)采用 Netty 進(jìn)行跨節(jié)點(diǎn)通信,它的 Netty Service 基于 Netty 框架二次封裝實(shí)現(xiàn)。

大數(shù)據(jù)計(jì)算往往采用多個計(jì)算節(jié)點(diǎn)和一個/N個匯總節(jié)點(diǎn)進(jìn)行分布式部署,各節(jié)點(diǎn)之間存在海量的數(shù)據(jù)交換。由于 Netty 的綜合性能是目前各個成熟 NIO 框架中最高的,因此,往往會被選中用作大數(shù)據(jù)各節(jié)點(diǎn)間的通信。

企業(yè)軟件:企業(yè)和 IT 集成需要 ESB,Netty 對多協(xié)議支持、私有協(xié)議定制的簡潔性和高性能是 ESB RPC 框架的首選通信組件。事實(shí)上,很多企業(yè)總線廠商會選擇 Netty 作為基礎(chǔ)通信組件,用于企業(yè)的 IT 集成。

通信行業(yè):Netty 的異步高性能、高可靠性和高成熟度的優(yōu)點(diǎn),使它在通信行業(yè)得到了大量的應(yīng)用。

使用傳統(tǒng)的 Socket 開發(fā)挺簡單的,我為什么要切換到 NIO 進(jìn)行編程呢?

首先我們看下傳統(tǒng)基于同步阻塞 IO(BIO)的線程模型圖:


圖、 同步阻塞 IO(BIO)線程模型圖

由上圖我們可以看出,傳統(tǒng)的同步阻塞 IO 通信存在如下幾個問題:

線程模型存在致命缺陷:一連接一線程的模型導(dǎo)致服務(wù)端無法承受大量客戶端的并發(fā)連接;

性能差:頻繁的線程上下文切換導(dǎo)致 CPU 利用效率不高;

可靠性差:由于所有的 IO 操作都是同步的,所以業(yè)務(wù)線程只要進(jìn)行 IO 操作,也會存在被同步阻塞的風(fēng)險(xiǎn),這會導(dǎo)致系統(tǒng)的可靠性差,依賴外部組件的處理能力和網(wǎng)絡(luò)的情況。

采用非阻塞 IO(NIO)之后,同步阻塞 IO 的三個缺陷都將迎刃而解:

Nio 采用 Reactor 模式,一個 Reactor 線程聚合一個多路復(fù)用器 Selector,它可以同時(shí)注冊、監(jiān)聽和輪詢成百上千個 Channel,一個 IO 線程可以同時(shí)并發(fā)處理N個客戶端連接,線程模型優(yōu)化為1:N(N < 進(jìn)程可用的最大句柄數(shù))或者 M : N (M通常為 CPU 核數(shù) + 1, N < 進(jìn)程可用的最大句柄數(shù));

由于 IO 線程總數(shù)有限,不會存在頻繁的 IO 線程之間上下文切換和競爭,CPU 利用率高;

所有的 IO 操作都是異步的,即使業(yè)務(wù)線程直接進(jìn)行 IO 操作,也不會被同步阻塞,系統(tǒng)不再依賴外部的網(wǎng)絡(luò)環(huán)境和外部應(yīng)用程序的處理性能。

?由于切換到 NIO 編程之后可以為系統(tǒng)帶來巨大的可靠性、性能提升,所以,目前采用 NIO 進(jìn)行通信已經(jīng)逐漸成為主流。

為什么不直接基于 JDK 的 NIO 類庫編程呢?


我們通過 JDK NIO 服務(wù)端和客戶端的工作時(shí)序圖來回答下這個問題:

?圖、JDK NIO 服務(wù)端創(chuàng)建和通信序列圖

即便拋開代碼和 NIO 類庫復(fù)雜性不談,一個高性能、高可靠性的 NIO 服務(wù)端開發(fā)和維護(hù)成本都是非常高的,開發(fā)者需要具有豐富的 NIO 編程經(jīng)驗(yàn)和網(wǎng)絡(luò)維護(hù)經(jīng)驗(yàn),很多時(shí)候甚至需要通過抓包來定位問題。也許開發(fā)出一套 NIO 程序需要 1 個月,但是它的穩(wěn)定很可能需要 1 年甚至更長的時(shí)間,這也就是為什么我不建議直接使用 JDK NIO 類庫進(jìn)行通信開發(fā)的一個重要原因。

下面再一起看下 JDK NIO 客戶端的通信時(shí)序圖:它同樣非常復(fù)雜。

?圖、JDK NIO 客戶端創(chuàng)建和通信序列圖

不選擇JAVA原生NIO和IO的原因

基于IO的經(jīng)典同步堵塞模型:

經(jīng)典的IO模型也就是傳統(tǒng)的服務(wù)器端同步阻塞I/O處理(也就是BIO,Blocking I/O)的經(jīng)典編程模型,當(dāng)我們每得到一個新的連接時(shí),就會開啟一個線程來處理這個連接的任務(wù)。之所以使用多線程,主要原因在于socket.accept()、socket.read()、socket.write()三個主要函數(shù)都是同步阻塞的,當(dāng)一個連接在處理I/O的時(shí)候,系統(tǒng)是阻塞的,如果是單線程的話必然就掛死在那里;但CPU是被釋放出來的,開啟多線程,就可以讓CPU去處理更多的事情。

因此這個模型最本質(zhì)的問題在于,嚴(yán)重依賴于線程。但線程是很”貴”的資源,主要表現(xiàn)在:

線程的創(chuàng)建和銷毀成本很高,在Linux這樣的操作系統(tǒng)中,線程本質(zhì)上就是一個進(jìn)程。創(chuàng)建和銷毀都是重量級的系統(tǒng)函數(shù)。

線程本身占用較大內(nèi)存,像Java的線程棧,一般至少分配512K~1M的空間,如果系統(tǒng)中的線程數(shù)過千,恐怕整個JVM的內(nèi)存都會被吃掉一半。

線程的切換成本是很高的。操作系統(tǒng)發(fā)生線程切換的時(shí)候,需要保留線程的上下文,然后執(zhí)行系統(tǒng)調(diào)用。如果線程數(shù)過高,可能執(zhí)行線程切換的時(shí)間甚至?xí)笥诰€程執(zhí)行的時(shí)間,這時(shí)候帶來的表現(xiàn)往往是系統(tǒng)load偏高、CPU sy使用率特別高(超過20%以上),導(dǎo)致系統(tǒng)幾乎陷入不可用的狀態(tài)。

容易造成鋸齒狀的系統(tǒng)負(fù)載。因?yàn)橄到y(tǒng)負(fù)載是用活動線程數(shù)或CPU核心數(shù),一旦線程數(shù)量高但外部網(wǎng)絡(luò)環(huán)境不是很穩(wěn)定,就很容易造成大量請求的結(jié)果同時(shí)返回,激活大量阻塞線程從而使系統(tǒng)負(fù)載壓力過大。

基于NIO的異步模型:

NIO是一種同步非阻塞的I/O模型,也是I/O多路復(fù)用的基礎(chǔ),而且已經(jīng)被越來越多地應(yīng)用到大型應(yīng)用服務(wù)器,成為解決高并發(fā)與大量連接、I/O處理問題的有效方式。

不使用NIO的原因:

NIO的類庫和API繁雜。需要很多額外的技能做鋪墊。例如需要很熟悉Java多線程編程、Selector線程模型。導(dǎo)致工作量和開發(fā)難度都非常大。

擴(kuò)展 ByteBuffer:NIO和Netty都有ByteBuffer來操作數(shù)據(jù),但是NIO的ByteBuffer長度固定而且操作復(fù)雜,許多操作甚至都需要自己實(shí)現(xiàn)。而且它的構(gòu)造函數(shù)是私有,不能擴(kuò)展。Netty 提供了自己的

ByteBuffer 實(shí)現(xiàn), Netty 通過一些簡單的 APIs 對 ByteBuffer 進(jìn)行構(gòu)造、使用和操作,以此來解決 NIO 中的一些限制。

NIO 對緩沖區(qū)的聚合和分散操作可能會操作內(nèi)存泄露,到j(luò)dk7才解決了內(nèi)存泄露的問題

存在臭名昭著的epoll bug,導(dǎo)致Selector空輪詢:這個bug會導(dǎo)致linux上導(dǎo)致cpu 100%

?為什么選擇Netty

API使用簡單,開發(fā)門檻低。

功能強(qiáng)大,預(yù)置了多種編解碼功能,支持多種協(xié)議開發(fā)。

定制能力強(qiáng),可以通過ChannelHadler進(jìn)行擴(kuò)展。

性能高,對比其它NIO框架,Netty綜合性能最優(yōu)。

經(jīng)歷了大規(guī)模的應(yīng)用驗(yàn)證。在互聯(lián)網(wǎng)、大數(shù)據(jù)、網(wǎng)絡(luò)游戲、企業(yè)應(yīng)用、電信軟件得到成功,很多著名的框架通信底層就用了Netty,比如Dubbo

穩(wěn)定,修復(fù)了NIO出現(xiàn)的所有Bug。

切換IO和NIO,因?yàn)镮O和NIO的API完全不同,相互切換非常困難。

類似的框架對比:

與Mina相比有什么優(yōu)勢?

1、都是Trustin Lee的作品,Netty更晚;

2、Mina將內(nèi)核和一些特性的聯(lián)系過于緊密,使得用戶在不需要這些特性的時(shí)候無法脫離,相比下性能會有所下降,Netty解決了這個設(shè)計(jì)問題;

3、Netty的文檔更清晰,很多Mina的特性在Netty里都有;

4、Netty更新周期更短,新版本的發(fā)布比較快;

5、它們的架構(gòu)差別不大,Mina靠apache生存,而Netty靠jboss,和jboss的結(jié)合度非常高,Netty有對google protocal buf的支持,有更完整的ioc容器支持(spring,guice,jbossmc和osgi);

6、Netty比Mina使用起來更簡單,Netty里你可以自定義的處理upstream events 或/和 downstream events,可以使用decoder和encoder來解碼和編碼發(fā)送內(nèi)容;

7、Netty和Mina在處理UDP時(shí)有一些不同,Netty將UDP無連接的特性暴露出來;而Mina對UDP進(jìn)行了高級層次的抽象,可以把UDP當(dāng)成”面向連接”的協(xié)議,而要Netty做到這一點(diǎn)比較困難。

在此我向大家推薦一個架構(gòu)學(xué)習(xí)交流群。交流學(xué)習(xí)群號:575745314 里面會分享一些資深架構(gòu)師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多

Netty多種IO方式的比較:

1、BIO(同步阻塞IO)

使用ServerSocket綁定IP地址和監(jiān)聽端口,客戶端發(fā)起連接,通過三次握手建立連接,用socket來進(jìn)行通信,通過輸入輸出流的方式來進(jìn)行同步阻塞的通信

每次客戶端發(fā)起連接請求,都會啟動一個線程

線程數(shù)量:客戶端并發(fā)訪問數(shù)為1:1,由于線程是JAVA虛擬機(jī)中非常寶貴的資源,一旦線程數(shù)急劇增加,系統(tǒng)性能會急劇下降,導(dǎo)致線程棧溢出,創(chuàng)建新的線程失敗,并最終導(dǎo)致宕機(jī)

所以在JDK1.4之前,人們想到了一種方法,即PIO方式

2、PIO(偽異步阻塞IO)

使用線程池來處理客戶端的請求

客戶端個數(shù):線程池最大線程數(shù)=M:N,其中M遠(yuǎn)大于N

在read和write的時(shí)候,還是IO阻塞的,只是把每個線程交由線程池來控制管理

3、NIO(異步阻塞IO)

用NIO方式處理IO

使用多路復(fù)用器Selector來輪詢每個通道Channel,當(dāng)通道中有事件時(shí)就通知處理,不會阻塞

使用相當(dāng)復(fù)雜

4、AIO(真正的異步非阻塞IO)

NIO2.0引入了新的異步通道的概念,不需要使用多路復(fù)用器(Selector)對注冊通道進(jìn)行輪詢即可實(shí)現(xiàn)異步讀寫,從而簡化了NIO編程模型

使用Netty框架進(jìn)行編程步驟

1、構(gòu)建事件處理池

2、使用引導(dǎo)程序關(guān)聯(lián)事件處理池、通道、事件處理器

3、綁定端口服務(wù)

4、等待操作完成

5、關(guān)閉事件處理池

幾種IO的功能和特性對比

按照書上的例子碼了一遍:

服務(wù)端:

服務(wù)端處理器:

客戶端:

客戶端處理器:

Netty的高性能架構(gòu)之道

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ù)分布式計(jì)算領(lǐng)域、游戲行業(yè)、通信行業(yè)等獲得了廣泛的應(yīng)用,一些業(yè)界著名的開源組件也基于Netty的NIO框架構(gòu)建。

為什么選擇Netty

Netty是業(yè)界最流行的NIO框架之一,它的健壯性、功能、性能、可定制性和可擴(kuò)展性在同類框架中都是首屈一指的,它已經(jīng)得到成百上千的商用項(xiàng)目驗(yàn)證,例如Hadoop的RPC框架avro使用Netty作為底層通信框架;很多其他業(yè)界主流的RPC框架,也使用Netty來構(gòu)建高性能的異步通信能力。

通過對Netty的分析,我們將它的優(yōu)點(diǎn)總結(jié)如下:

1.API使用簡單,開發(fā)門檻低;

2.功能強(qiáng)大,預(yù)置了多種編解碼功能,支持多種主流協(xié)議;

3.定制能力強(qiáng),可以通過ChannelHandler對通信框架進(jìn)行靈活地?cái)U(kuò)展;

4.性能高,通過與其他業(yè)界主流的NIO框架對比,Netty的綜合性能最優(yōu);

5.成熟、穩(wěn)定,Netty修復(fù)了已經(jīng)發(fā)現(xiàn)的所有JDK NIO BUG,業(yè)務(wù)開發(fā)人員不需要再為NIO的BUG而煩惱;

6.社區(qū)活躍,版本迭代周期短,發(fā)現(xiàn)的BUG可以被及時(shí)修復(fù),同時(shí),更多的新功能會加入;

經(jīng)歷了大規(guī)模的商業(yè)應(yīng)用考驗(yàn),質(zhì)量得到驗(yàn)證。在互聯(lián)網(wǎng)、大數(shù)據(jù)、網(wǎng)絡(luò)游戲、企業(yè)應(yīng)用、電信軟件等眾多行業(yè)得到成功商用,證明了它已經(jīng)完全能夠滿足不同行業(yè)的商業(yè)應(yīng)用了。

在此我向大家推薦一個架構(gòu)學(xué)習(xí)交流群。交流學(xué)習(xí)群號:575745314 里面會分享一些資深架構(gòu)師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多

Netty架構(gòu)分析

Netty 采用了比較典型的三層網(wǎng)絡(luò)架構(gòu)進(jìn)行設(shè)計(jì),邏輯架構(gòu)圖如下所示:

第一層:Reactor 通信調(diào)度層,它由一系列輔助類完成,包括 Reactor 線程 NioEventLoop 以及其父類、NioSocketChannel/NioServerSocketChannel 以及其父 類、ByteBuffer 以及由其衍生出來的各種 Buffer、Unsafe 以及其衍生出的各種內(nèi) 部類等。該層的主要職責(zé)就是監(jiān)聽網(wǎng)絡(luò)的讀寫和連接操作,負(fù)責(zé)將網(wǎng)絡(luò)層的數(shù)據(jù) 讀取到內(nèi)存緩沖區(qū)中,然后觸發(fā)各種網(wǎng)絡(luò)事件,例如連接創(chuàng)建、連接激活、讀事 件、寫事件等等,將這些事件觸發(fā)到 PipeLine 中,由 PipeLine 充當(dāng)?shù)穆氊?zé)鏈來 進(jìn)行后續(xù)的處理。

第二層:職責(zé)鏈 PipeLine,它負(fù)責(zé)事件在職責(zé)鏈中的有序傳播,同時(shí)負(fù)責(zé)動態(tài)的 編排職責(zé)鏈,職責(zé)鏈可以選擇監(jiān)聽和處理自己關(guān)心的事件,它可以攔截處理和向 后/向前傳播事件,不同的應(yīng)用的 Handler 節(jié)點(diǎn)的功能也不同,通常情況下,往往 會開發(fā)編解碼 Hanlder 用于消息的編解碼,它可以將外部的協(xié)議消息轉(zhuǎn)換成內(nèi)部 的 POJO 對象,這樣上層業(yè)務(wù)側(cè)只需要關(guān)心處理業(yè)務(wù)邏輯即可,不需要感知底層 的協(xié)議差異和線程模型差異,實(shí)現(xiàn)了架構(gòu)層面的分層隔離。

第三層:業(yè)務(wù)邏輯處理層,可以分為兩類:

1.純粹的業(yè)務(wù)邏輯 處理,例如訂單處理。

2.應(yīng)用層協(xié)議管理,例如HTTP協(xié)議、FTP協(xié)議等。

接下來,我從影響通信性能的三個方面(I/O模型、線程調(diào)度模型、序列化方式)來談?wù)凬etty的架構(gòu)。

I/O模型

傳統(tǒng)同步阻塞I/O模式如下圖所示:

它的弊端有很多:

1.性能問題:一連接一線程模型導(dǎo)致服務(wù)端的并發(fā)接入數(shù)和系統(tǒng)吞吐量受到極大限制;

2.可靠性問題:由于I/O操作采用同步阻塞模式,當(dāng)網(wǎng)絡(luò)擁塞或者通信對端處理緩慢會導(dǎo)致I/O線程被掛住,阻塞時(shí)間無法預(yù)測;

3.可維護(hù)性問題:I/O線程數(shù)無法有效控制、資源無法有效共享(多線程并發(fā)問題),系統(tǒng)可維護(hù)性差;

幾種I/O模型的功能和特性對比:

Netty的I/O模型基于非阻塞I/O實(shí)現(xiàn),底層依賴的是JDK NIO框架的Selector。

Selector提供選擇已經(jīng)就緒的任務(wù)的能力。簡單來講,Selector會不斷地輪詢注冊在其上的Channel,如果某個Channel上面有新的TCP連接接入、讀和寫事件,這個Channel就處于就緒狀態(tài),會被Selector輪詢出來,然后通過SelectionKey可以獲取就緒Channel的集合,進(jìn)行后續(xù)的I/O操作。

一個多路復(fù)用器Selector可以同時(shí)輪詢多個Channel,由于JDK1.5_update10版本(+)使用了epoll()代替?zhèn)鹘y(tǒng)的select實(shí)現(xiàn),所以它并沒有最大連接句柄1024/2048的限制。這也就意味著只需要一個線程負(fù)責(zé)Selector的輪詢,就可以接入成千上萬的客戶端,這確實(shí)是個非常巨大的技術(shù)進(jìn)步。

使用非阻塞I/O模型之后,Netty解決了傳統(tǒng)同步阻塞I/O帶來的性能、吞吐量和可靠性問題。

線程調(diào)度模型

常用的Reactor線程模型有三種,分別如下:

1.Reactor單線程模型:Reactor單線程模型,指的是所有的I/O操作都在同一個NIO線程上面完成。對于一些小容量應(yīng)用場景,可以使用單線程模型。

2.Reactor多線程模型:Rector多線程模型與單線程模型最大的區(qū)別就是有一組NIO線程處理I/O操作。主要用于高并發(fā)、大業(yè)務(wù)量場景。

3.主從Reactor多線程模型:主從Reactor線程模型的特點(diǎn)是服務(wù)端用于接收客戶端連接的不再是個1個單獨(dú)的NIO線程,而是一個獨(dú)立的NIO線程池。利用主從NIO線程模型,可以解決1個服務(wù)端監(jiān)聽線程無法有效處理所有客戶端連接的性能不足問題。

事實(shí)上,Netty的線程模型并非固定不變,通過在啟動輔助類中創(chuàng)建不同的EventLoopGroup實(shí)例并通過適當(dāng)?shù)膮?shù)配置,就可以支持上述三種Reactor線程模型。

在大多數(shù)場景下,并行多線程處理可以提升系統(tǒng)的并發(fā)性能。但是,如果對于共享資源的并發(fā)訪問處理不當(dāng),會帶來嚴(yán)重的鎖競爭,這最終會導(dǎo)致性能的下降。為了盡可能的避免鎖競爭帶來的性能損耗,可以通過串行化設(shè)計(jì),即消息的處理盡可能在同一個線程內(nèi)完成,期間不進(jìn)行線程切換,這樣就避免了多線程競爭和同步鎖。

為了盡可能提升性能,Netty采用了串行無鎖化設(shè)計(jì),在I/O線程內(nèi)部進(jìn)行串行操作,避免多線程競爭導(dǎo)致的性能下降。表面上看,串行化設(shè)計(jì)似乎CPU利用率不高,并發(fā)程度不夠。但是,通過調(diào)整NIO線程池的線程參數(shù),可以同時(shí)啟動多個串行化的線程并行運(yùn)行,這種局部無鎖化的串行線程設(shè)計(jì)相比一個隊(duì)列-多個工作線程模型性能更優(yōu)。

序列化方式

影響序列化性能的關(guān)鍵因素總結(jié)如下:

1.序列化后的碼流大?。ňW(wǎng)絡(luò)帶寬占用)

2.序列化&反序列化的性能(CPU資源占用)

3.并發(fā)調(diào)用的性能表現(xiàn):穩(wěn)定性、線性增長、偶現(xiàn)的時(shí)延毛刺等

對Java序列化和二進(jìn)制編碼分別進(jìn)行性能測試,編碼100萬次,測試結(jié)果表明:Java序列化的性能只有二進(jìn)制編碼的6.17%左右。

Netty默認(rèn)提供了對Google Protobuf的支持,通過擴(kuò)展Netty的編解碼接口,用戶可以實(shí)現(xiàn)其它的高性能序列化框架,例如Thrift的壓縮二進(jìn)制編解碼框架。

不同的應(yīng)用場景對序列化框架的需求也不同,對于高性能應(yīng)用場景Netty默認(rèn)提供了Google的Protobuf二進(jìn)制序列化框架,如果用戶對其它二進(jìn)制序列化框架有需求,也可以基于Netty提供的編解碼框架擴(kuò)展實(shí)現(xiàn)。

Netty架構(gòu)剖析之可靠性

Netty面臨的可靠性挑戰(zhàn):

1.作為RPC框架的基礎(chǔ)網(wǎng)絡(luò)通信框架,一旦故障將導(dǎo)致無法進(jìn)行遠(yuǎn)程服務(wù)(接口)調(diào)用。

2.作為應(yīng)用層協(xié)議的基礎(chǔ)通信框架,一旦故障將導(dǎo)致應(yīng)用協(xié)議棧無法正常工作。

3.網(wǎng)絡(luò)環(huán)境復(fù)雜(例如手游或者推送服務(wù)的GSM/3G/WIFI網(wǎng)絡(luò)),故障不可避免,業(yè)務(wù)卻不能中斷。

從應(yīng)用場景看,Netty是基礎(chǔ)的通信框架,一旦出現(xiàn)Bug,輕則需要重啟應(yīng)用,重則可能導(dǎo)致整個業(yè)務(wù)中斷。它的可靠性會影響整個業(yè)務(wù)集群的數(shù)據(jù)通信和交換,在當(dāng)今以分布式為主的軟件架構(gòu)體系中,通信中斷就意味著整個業(yè)務(wù)中斷,分布式架構(gòu)下對通信的可靠性要求非常高。

從運(yùn)行環(huán)境看,Netty會面臨惡劣的網(wǎng)絡(luò)環(huán)境,這就要求它自身的可靠性要足夠好,平臺能夠解決的可靠性問題需要由Netty自身來解決,否則會導(dǎo)致上層用戶關(guān)注過多的底層故障,這將降低Netty的易用性,同時(shí)增加用戶的開發(fā)和運(yùn)維成本。

Netty的可靠性是如此重要,它的任何故障都可能會導(dǎo)致業(yè)務(wù)中斷,蒙受巨大的經(jīng)濟(jì)損失。因此,Netty在版本的迭代中不斷加入新的可靠性特性來滿足用戶日益增長的高可靠和健壯性需求。

鏈路有效性檢測

Netty提供的心跳檢測機(jī)制分為三種:

1.讀空閑,鏈路持續(xù)時(shí)間t沒有讀取到任何消息;

2.寫空閑,鏈路持續(xù)時(shí)間t沒有發(fā)送任何消息;

3.讀寫空閑,鏈路持續(xù)時(shí)間t沒有接收或者發(fā)送任何消息。

當(dāng)網(wǎng)絡(luò)發(fā)生單通、連接被防火墻Hang住、長時(shí)間GC或者通信線程發(fā)生非預(yù)期異常時(shí),會導(dǎo)致鏈路不可用且不易被及時(shí)發(fā)現(xiàn)。特別是異常發(fā)生在凌晨業(yè)務(wù)低谷期間,當(dāng)早晨業(yè)務(wù)高峰期到來時(shí),由于鏈路不可用會導(dǎo)致瞬間的大批量業(yè)務(wù)失敗或者超時(shí),這將對系統(tǒng)的可靠性產(chǎn)生重大的威脅。

從技術(shù)層面看,要解決鏈路的可靠性問題,必須周期性的對鏈路進(jìn)行有效性檢測。目前最流行和通用的做法就是心跳檢測。

心跳檢測機(jī)制分為三個層面:

1.TCP層面的心跳檢測,即TCP的Keep-Alive機(jī)制,它的作用域是整個TCP協(xié)議棧;

2.協(xié)議層的心跳檢測,主要存在于長連接協(xié)議中。例如SMPP協(xié)議;

3.應(yīng)用層的心跳檢測,它主要由各業(yè)務(wù)產(chǎn)品通過約定方式定時(shí)給對方發(fā)送心跳消息實(shí)現(xiàn)。

心跳檢測的目的就是確認(rèn)當(dāng)前鏈路可用,對方活著并且能夠正常接收和發(fā)送消息。做為高可靠的NIO框架,Netty也提供了基于鏈路空閑的心跳檢測機(jī)制:

1.讀空閑,鏈路持續(xù)時(shí)間t沒有讀取到任何消息;

2.寫空閑,鏈路持續(xù)時(shí)間t沒有發(fā)送任何消息;

3.讀寫空閑,鏈路持續(xù)時(shí)間t沒有接收或者發(fā)送任何消息。

流量整形

流量整形(Traffic Shaping)是一種主動調(diào)整流量輸出速率的措施。Netty的流量整形有兩個作用:

1.防止由于上下游網(wǎng)元性能不均衡導(dǎo)致下游網(wǎng)元被壓垮,業(yè)務(wù)流程中斷;

2.防止由于通信模塊接收消息過快,后端業(yè)務(wù)線程處理不及時(shí)導(dǎo)致的“撐死”問題。

流量整形的原理示意圖如下:

流量整形(Traffic Shaping)是一種主動調(diào)整流量輸出速率的措施。一個典型應(yīng)用是基于下游網(wǎng)絡(luò)結(jié)點(diǎn)的TP指標(biāo)來控制本地流量的輸出。流量整形與流量監(jiān)管的主要區(qū)別在于,流量整形對流量監(jiān)管中需要丟棄的報(bào)文進(jìn)行緩存——通常是將它們放入緩沖區(qū)或隊(duì)列內(nèi),也稱流量整形(Traffic Shaping,簡稱TS)。當(dāng)令牌桶有足夠的令牌時(shí),再均勻的向外發(fā)送這些被緩存的報(bào)文。流量整形與流量監(jiān)管的另一區(qū)別是,整形可能會增加延遲,而監(jiān)管幾乎不引入額外的延遲。

Netty支持兩種流量整形模式:

1.全局流量整形:全局流量整形的作用范圍是進(jìn)程級的,無論你創(chuàng)建了多少個Channel,它的作用域針對所有的Channel。用戶可以通過參數(shù)設(shè)置:報(bào)文的接收速率、報(bào)文的發(fā)送速率、整形周期。

2.鏈路級流量整形:單鏈路流量整形與全局流量整形的最大區(qū)別就是它以單個鏈路為作用域,可以對不同的鏈路設(shè)置不同的整形策略。

在此我向大家推薦一個架構(gòu)學(xué)習(xí)交流群。交流學(xué)習(xí)群號:575745314 里面會分享一些資深架構(gòu)師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多

優(yōu)雅停機(jī)

Netty的優(yōu)雅停機(jī)三部曲:

1.不再接收新消息

2.退出前的預(yù)處理操作

3.資源的釋放操作

Java的優(yōu)雅停機(jī)通常通過注冊JDK的ShutdownHook來實(shí)現(xiàn),當(dāng)系統(tǒng)接收到退出指令后,首先標(biāo)記系統(tǒng)處于退出狀態(tài),不再接收新的消息,然后將積壓的消息處理完,最后調(diào)用資源回收接口將資源銷毀,最后各線程退出執(zhí)行。

通常優(yōu)雅退出需要有超時(shí)控制機(jī)制,例如30S,如果到達(dá)超時(shí)時(shí)間仍然沒有完成退出前的資源回收等操作,則由停機(jī)腳本直接調(diào)用kill -9 pid,強(qiáng)制退出。

在實(shí)際項(xiàng)目中,Netty作為高性能的異步NIO通信框架,往往用作基礎(chǔ)通信框架負(fù)責(zé)各種協(xié)議的接入、解析和調(diào)度等,例如在RPC和分布式服務(wù)框架中,往往會使用Netty作為內(nèi)部私有協(xié)議的基礎(chǔ)通信框架。 當(dāng)應(yīng)用進(jìn)程優(yōu)雅退出時(shí),作為通信框架的Netty也需要優(yōu)雅退出,主要原因如下:

盡快的釋放NIO線程、句柄等資源;

如果使用flush做批量消息發(fā)送,需要將積攢在發(fā)送隊(duì)列中的待發(fā)送消息發(fā)送完成;

正在write或者read的消息,需要繼續(xù)處理;

設(shè)置在NioEventLoop線程調(diào)度器中的定時(shí)任務(wù),需要執(zhí)行或者清理。

Netty架構(gòu)剖析之安全性

Netty面臨的安全挑戰(zhàn):

對第三方開放

作為應(yīng)用層協(xié)議的基礎(chǔ)通信框架

安全威脅場景分析:

對第三方開放的通信框架:如果使用Netty做RPC框架或者私有協(xié)議棧,RPC框架面向非授信的第三方開放,例如將內(nèi)部的一些能力通過服務(wù)對外開放出去,此時(shí)就需要進(jìn)行安全認(rèn)證,如果開放的是公網(wǎng)IP,對于安全性要求非常高的一些服務(wù),例如在線支付、訂購等,需要通過SSL/TLS進(jìn)行通信。

應(yīng)用層協(xié)議的安全性。作為高性能、異步事件驅(qū)動的NIO框架,Netty非常適合構(gòu)建上層的應(yīng)用層協(xié)議。由于絕大多數(shù)應(yīng)用層協(xié)議都是公有的,這意味著底層的Netty需要向上層提供通信層的安全傳輸功能。

SSL/TLS

Netty安全傳輸特性:

1.支持SSL V2和V3

2.支持TLS

3.支持SSL單向認(rèn)證、雙向認(rèn)證和第三方CA認(rèn)證。

SSL單向認(rèn)證流程圖如下:

Netty通過SslHandler提供了對SSL的支持,它支持的SSL協(xié)議類型包括:SSL V2、SSL V3和TLS。

單向認(rèn)證:單向認(rèn)證,即客戶端只驗(yàn)證服務(wù)端的合法性,服務(wù)端不驗(yàn)證客戶端。

雙向認(rèn)證:與單向認(rèn)證不同的是服務(wù)端也需要對客戶端進(jìn)行安全認(rèn)證。這就意味著客戶端的自簽名證書也需要導(dǎo)入到服務(wù)端的數(shù)字證書倉庫中。

CA認(rèn)證:基于自簽名的SSL雙向認(rèn)證,只要客戶端或者服務(wù)端修改了密鑰和證書,就需要重新進(jìn)行簽名和證書交換,這種調(diào)試和維護(hù)工作量是非常大的。因此,在實(shí)際的商用系統(tǒng)中往往會使用第三方CA證書頒發(fā)機(jī)構(gòu)進(jìn)行簽名和驗(yàn)證。我們的瀏覽器就保存了幾個常用的CA_ROOT。每次連接到網(wǎng)站時(shí)只要這個網(wǎng)站的證書是經(jīng)過這些CA_ROOT簽名過的。就可以通過驗(yàn)證了。

可擴(kuò)展的安全特性

通過Netty的擴(kuò)展特性,可以自定義安全策略:

1.IP地址黑名單機(jī)制

2.接入認(rèn)證

3.敏感信息加密或者過濾機(jī)制

IP地址黑名單是比較常用的弱安全保護(hù)策略,它的特點(diǎn)就是服務(wù)端在與客戶端通信的過程中,對客戶端的IP地址進(jìn)行校驗(yàn),如果發(fā)現(xiàn)對方IP在黑名單列表中,則拒絕與其通信,關(guān)閉鏈路。

接入認(rèn)證策略非常多,通常是較強(qiáng)的安全認(rèn)證策略,例如基于用戶名+密碼的認(rèn)證,認(rèn)證內(nèi)容往往采用加密的方式,例如Base64+AES等。

Netty架構(gòu)剖析之?dāng)U展性

通過Netty的擴(kuò)展特性,可以自定義安全策略:

1.線程模型可擴(kuò)展

2.序列化方式可擴(kuò)展

3.上層協(xié)議??蓴U(kuò)展

4.提供大量的網(wǎng)絡(luò)事件切面,方便用戶功能擴(kuò)展

Netty的架構(gòu)可擴(kuò)展性設(shè)計(jì)理念如下:

5.判斷擴(kuò)展點(diǎn),事先預(yù)留相關(guān)擴(kuò)展接口,給用戶二次定制和擴(kuò)展使用;

6.主要功能點(diǎn)都基于接口編程,方便用戶定制和擴(kuò)展。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容