一、把握 Netty 整体架构脉络
一、整體結(jié)構(gòu)
Netty 是一個(gè) NIO 客戶端服務(wù)器框架,可快速輕松地開發(fā)網(wǎng)絡(luò)應(yīng)用程序,例如協(xié)議服務(wù)器和客戶端。它極大地簡(jiǎn)化和簡(jiǎn)化了網(wǎng)絡(luò)編程,例如 TCP 和 UDP 套接字服務(wù)器。“快速簡(jiǎn)便”并不意味著最終的應(yīng)用程序?qū)⒃馐芸删S護(hù)性或性能問題的困擾。Netty 經(jīng)過精心設(shè)計(jì),結(jié)合了許多協(xié)議(例如FTP,SMTP,HTTP 以及各種基于二進(jìn)制和文本的舊式協(xié)議)的實(shí)施經(jīng)驗(yàn)。結(jié)果,Netty 成功地找到了一種無需妥協(xié)即可輕松實(shí)現(xiàn)開發(fā),性能,穩(wěn)定性和靈活性的方法。
Netty 是一個(gè)設(shè)計(jì)非常用心的網(wǎng)絡(luò)基礎(chǔ)組件,結(jié)構(gòu)一共分為三個(gè)模塊:
- Core 核心層
提供了底層網(wǎng)絡(luò)通信的通用抽象和實(shí)現(xiàn),包括可擴(kuò)展的事件模型、通用的通信 API、支持零拷貝的 ByteBuf 等。 - Protocol Support 協(xié)議支持層
覆蓋了主流協(xié)議的編解碼實(shí)現(xiàn),如HTTP、SSL、Protobuf、壓縮、大文件傳輸、WebSocket、文本、二進(jìn)制等主流協(xié)議,此外 Netty 還支持自定義應(yīng)用層協(xié)議。 - Transport Service 傳輸服務(wù)層
提供了網(wǎng)絡(luò)傳輸能力的定義和實(shí)現(xiàn)方法,支持 Socket、HTTP 隧道、虛擬機(jī)管道等傳輸方式。Netty 對(duì) TCP、UDP 等數(shù)據(jù)傳輸做了抽象和封裝。具備較高的通用性和可擴(kuò)展性,它不僅是一個(gè)優(yōu)秀的網(wǎng)絡(luò)框架,還可以作為網(wǎng)絡(luò)編程的工具箱。
核心邏輯架構(gòu)
- Reactor 通信調(diào)度層:由一系列輔助類組成,包括 Reactor 線程 NioEventLoop 及其父類,NioSocketChannel 和 NioServerSocketChannel等等。該層的職責(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 管理的職責(zé)鏈來進(jìn)行后續(xù)的處理。
- 職責(zé)鏈 ChannelPipeline:負(fù)責(zé)事件在職責(zé)鏈中的有序傳播,以及負(fù)責(zé)動(dòng)態(tài)地編排職責(zé)鏈。職責(zé)鏈可以選擇監(jiān)聽和處理自己關(guān)心的事件,攔截處理和向后傳播事件。
- 業(yè)務(wù)邏輯編排層:業(yè)務(wù)邏輯編排層通常有兩類,一類是純粹的業(yè)務(wù)邏輯編排,一類是應(yīng)用層協(xié)議插件,用于特定協(xié)議相關(guān)的會(huì)話和鏈路管理。由于應(yīng)用層協(xié)議棧往往是開發(fā)一次到處運(yùn)行,并且變動(dòng)較小,故而將應(yīng)用協(xié)議到 POJO 的轉(zhuǎn)變和上層業(yè)務(wù)放到不同的 ChannelHandler 中,就可以實(shí)現(xiàn)協(xié)議層和業(yè)務(wù)邏輯層的隔離,實(shí)現(xiàn)架構(gòu)層面的分層隔離。
二、邏輯架構(gòu)
核心組件包含BootStrap、ServerBootStrap、Channel三個(gè)組件
- BootStrap & ServerBootStrap
Bootstrap 是“引導(dǎo)”的意思,它主要負(fù)責(zé)整個(gè) Netty 程序的啟動(dòng)、初始化、服務(wù)器連接等過程,它相當(dāng)于一條主線,串聯(lián)了 Netty 的其他核心組件。
Netty 中的引導(dǎo)器共分為兩種類型:
- 一個(gè)為用于客戶端引導(dǎo)的 Bootstrap
- 另一個(gè)為用于服務(wù)端引導(dǎo)的ServerBootStrap,它們都繼承自抽象類 AbstractBootstrap。
Bootstrap與ServerBootstrap 的區(qū)別?
- Bootstrap 可用于連接遠(yuǎn)端服務(wù)器,只綁定一個(gè) EventLoopGroup。
- ServerBootStrap則用于服務(wù)端啟動(dòng)綁定本地端口,會(huì)綁定兩個(gè) EventLoopGroup,這兩個(gè) EventLoopGroup 通常稱為 Boss 和 Worker。這里的 Boss 和 Worker 可以理解為“老板”和“員工”的關(guān)系。每個(gè)服務(wù)器中都會(huì)有一個(gè) Boss,也會(huì)有一群做事情的Worker。Boss 會(huì)不停地接收新的連接,然后將連接分配給一個(gè)個(gè) Worker 處理連接。
- Channel
Channel的字面意思是“通道”,是網(wǎng)絡(luò)通信的載體,提供了基本的 API 用于網(wǎng)絡(luò) I/O 操作,如 register、bind、connect、read、write、flush 等。
- Netty實(shí)現(xiàn)的 Channel 是以 JDK NIO Channel 為基礎(chǔ)的
- 相比較于 JDK NIO,Netty 的 Channel 提供了更高層次的抽象,同時(shí)屏蔽了底層 Socket 的復(fù)雜性,賦予了 Channel 更加強(qiáng)大的功能。
常用分類:
- NioServerSocketChannel 異步 TCP 服務(wù)端
- NioSocketChannel 異步 TCP 客戶端
- OioServerSocketChannel 同步 TCP 服務(wù)端
- OioSocketChannel 同步 TCP 客戶端
- NioDatagramChannel 異步 UDP 連接
- OioDatagramChannel 同步 UDP 連接
基本狀態(tài):
- 連接建立
- 連接注冊(cè)
- 數(shù)據(jù)讀寫
- 連接銷毀
常見事件:
事件調(diào)度層的職責(zé)是通過 Reactor 線程模型對(duì)各類事件進(jìn)行聚合處理,通過 Selector 主循環(huán)線程集成多種事件( I/O 事件、信號(hào)事件、定時(shí)事件等),實(shí)際的業(yè)務(wù)處理邏輯是交由服務(wù)編排層中相關(guān)的 Handler 完成。
- EventLoopGroup & EventLoop
EventLoop 則是 EventLoopGroup 的子接口,可以將其看做只有一個(gè)EventLoop的Group.
- NioEventLoopGroup 是 Netty 中最被推薦使用的線程模型,基于 NIO 模型開發(fā),每個(gè)線程負(fù)責(zé)處理多個(gè)Channel,而同一個(gè) Channel 只會(huì)對(duì)應(yīng)一個(gè)線程。
EventLoopGroup 是 Netty Reactor 線程模型的具體實(shí)現(xiàn)方式。
- Reactor 的三種線程模型
負(fù)責(zé)組裝各類服務(wù),它是 Netty 的核心處理鏈,用以實(shí)現(xiàn)網(wǎng)絡(luò)事件的動(dòng)態(tài)編排和有序傳播。核心組件包括 ChannelPipeline、ChannelHandler、ChannelHandlerContext。
- ChannelPipeline
負(fù)責(zé)組裝各種 ChannelHandler,可以理解為ChannelHandler 的實(shí)例列表——內(nèi)部通過雙向鏈表將不同的 ChannelHandler 鏈接在一起。當(dāng) I/O 讀寫事件觸發(fā)時(shí),ChannelPipeline 會(huì)依次調(diào)用 ChannelHandler 列表對(duì) Channel 的數(shù)據(jù)進(jìn)行攔截和處理。ChannelPipeline 是線程安全的,因?yàn)槊恳粋€(gè)新的 Channel 都會(huì)對(duì)應(yīng)綁定一個(gè)新的 ChannelPipeline。一個(gè) ChannelPipeline 關(guān)聯(lián)一個(gè) EventLoop,一個(gè) EventLoop 僅會(huì)綁定一個(gè)線程。
ChannelPipeline 中包含入站 ChannelInboundHandler 和出站 ChannelOutboundHandler 兩種處理器。客戶端和服務(wù)端都有各自的 ChannelPipeline。以客戶端為例,數(shù)據(jù)從客戶端發(fā)向服務(wù)端,該過程稱為出站,反之則稱為入站。數(shù)據(jù)入站會(huì)由一系列 InBoundHandler 處理,然后再以相反方向的 OutBoundHandler 處理后完成出站。我們經(jīng)常使用的編碼 Encoder 是出站操作,解碼 Decoder 是入站操作。服務(wù)端接收到客戶端數(shù)據(jù)后,需要先經(jīng)過 Decoder 入站處理后,再通過 Encoder 出站通知客戶端。所以客戶端和服務(wù)端一次完整的請(qǐng)求應(yīng)答過程可以分為三個(gè)步驟:客戶端出站(請(qǐng)求數(shù)據(jù))、服務(wù)端入站(解析數(shù)據(jù)并執(zhí)行業(yè)務(wù)邏輯)、服務(wù)端出站(響應(yīng)結(jié)果)。
- ChannelHandler&ChannelHandlerContext
每創(chuàng)建一個(gè) Channel 都會(huì)綁定一個(gè)新的 ChannelPipeline,ChannelPipeline 中每加入一個(gè)ChannelHandler 都會(huì)綁定一個(gè) ChannelHandlerContext。ChannelHandlerContext 用于保存
ChannelHandler 上下文。
通過 ChannelHandlerContext 我們可以知道 ChannelPipeline 和 ChannelHandler 的關(guān)聯(lián)關(guān)系。ChannelHandlerContext 可以實(shí)現(xiàn) ChannelHandler 之間的交互,ChannelHandlerContext 包含了 ChannelHandler 生命周期的所有事件,如 connect、bind、read、flush、write、close 等。
三、組件關(guān)系梳理
四、Netty 的數(shù)據(jù)容器(ByteBuf)
前面介紹了 Netty 的幾個(gè)核心組件,服務(wù)器在數(shù)據(jù)傳輸?shù)臅r(shí)候,產(chǎn)生事件,并且對(duì)事件進(jìn)行監(jiān)控和處理。接下來看看數(shù)據(jù)是如何存放以及是如何讀寫的。Netty 將 ByteBuf 作為數(shù)據(jù)容器,來存放數(shù)據(jù)。
從結(jié)構(gòu)上來說,ByteBuf 由一串字節(jié)數(shù)組構(gòu)成。數(shù)組中每個(gè)字節(jié)用來存放信息。ByteBuf 提供了兩個(gè)索引,一個(gè)用于讀取數(shù)據(jù),一個(gè)用于寫入數(shù)據(jù)。這兩個(gè)索引通過在字節(jié)數(shù)組中移動(dòng),來定位需要讀或者寫信息的位置。當(dāng)從 ByteBuf 讀取時(shí),它的 readerIndex(讀索引)將會(huì)根據(jù)讀取的字節(jié)數(shù)遞增。同樣,當(dāng)寫 ByteBuf 時(shí),它的 writerIndex 也會(huì)根據(jù)寫入的字節(jié)數(shù)進(jìn)行遞增。ByteBuf 讀寫索引圖例
需要注意的是極限的情況是 readerIndex 剛好讀到了 writerIndex 寫入的地方。如果 readerIndex 超過了 writerIndex 的時(shí)候,Netty 會(huì)拋出 IndexOutOf-BoundsException 異常。
談了 ByteBuf 的工作原理以后,再來看看它的使用模式。
根據(jù)存放緩沖區(qū)的不同分為三類:
- 堆緩沖區(qū),ByteBuf 將數(shù)據(jù)存儲(chǔ)在 JVM 的堆中,通過數(shù)組實(shí)現(xiàn),可以做到快速分配。 由于在堆上被 JVM管理,在不被使用時(shí)可以快速釋放。可以通過 ByteBuf.array() 來獲取 byte[] 數(shù)據(jù)。
- 直接緩沖區(qū),在 JVM的堆之外直接分配內(nèi)存,用來存儲(chǔ)數(shù)據(jù)。其不占用堆空間,使用時(shí)需要考慮內(nèi)存容量。 它在使用 Socket傳遞時(shí)性能較好,因?yàn)殚g接從緩沖區(qū)發(fā)送數(shù)據(jù),在發(fā)送之前 JVM 會(huì)先將數(shù)據(jù)復(fù)制到直接緩沖區(qū)再進(jìn)行發(fā)送。由于,直接緩沖區(qū)的數(shù)據(jù)分配在堆之外,通過 JVM 進(jìn)行垃圾回收,并且分配時(shí)也需要做復(fù)制的操作,因此使用成本較高。
- 復(fù)合緩沖區(qū),顧名思義就是將上述兩類緩沖區(qū)聚合在一起。Netty 提供了一個(gè)CompsiteByteBuf,可以將堆緩沖區(qū)和直接緩沖區(qū)的數(shù)據(jù)放在一起,讓使用更加方便。
聊完了結(jié)構(gòu)和使用模式,再來看看 ByteBuf 是如何分配緩沖區(qū)的數(shù)據(jù)的。
Netty 提供了兩種 ByteBufAllocator 的實(shí)現(xiàn),他們分別是:
- PooledByteBufAllocator,實(shí)現(xiàn)了 ByteBuf 的對(duì)象的池化,提高性能減少內(nèi)存碎片。
- Unpooled-ByteBufAllocator,沒有實(shí)現(xiàn)對(duì)象的池化,每次會(huì)生成新的對(duì)象實(shí)例。
對(duì)象池化的技術(shù)和線程池,比較相似,主要目的是提高內(nèi)存的使用率。池化的簡(jiǎn)單實(shí)現(xiàn)思路,是在 JVM 堆內(nèi)存上構(gòu)建一層內(nèi)存池,通過 allocate 方法獲取內(nèi)存池中的空間,通過 release 方法將空間歸還給內(nèi)存池。對(duì)象的生成和銷毀,會(huì)大量地調(diào)用 allocate 和 release 方法,因此內(nèi)存池面臨碎片空間回收的問題,在頻繁申請(qǐng)和釋放空間后,內(nèi)存池需要保證連續(xù)的內(nèi)存空間,用于對(duì)象的分配。
五、源碼結(jié)構(gòu)
1。 核心層模塊
- netty-common
是 Netty 的核心基礎(chǔ)包,提供了豐富的工具類
- 通用工具類:比如定時(shí)器工具 TimerTask、時(shí)間輪 HashedWheelTimer 等。
- 自定義并發(fā)包:比如異步模型Future & Promise、相比 JDK 增強(qiáng)的 FastThreadLocal 等。
- netty-buffer
實(shí)現(xiàn)了的一個(gè)更加完備的ByteBuf 工具類,用于網(wǎng)絡(luò)通信中的數(shù)據(jù)載體 - netty-resover
提供了一些有關(guān)基礎(chǔ)設(shè)施的解析工具,包括 IP Address、Hostname、DNS 等。
- netty-codec
負(fù)責(zé)編解碼工作,通過編解碼實(shí)現(xiàn)原始字節(jié)數(shù)據(jù)與業(yè)務(wù)實(shí)體對(duì)象之間的相互轉(zhuǎn)化。提供了抽象的編解碼類 ByteToMessageDecoder 和 MessageToByteEncoder,通過繼承這兩個(gè)類我們可以輕松實(shí)現(xiàn)自定義的編解碼邏輯。 - netty-handler
netty-handler 模塊提供了開箱即用的 ChannelHandler 實(shí)現(xiàn)類,例如日志、IP 過濾、流量整形等,如果需要這些功能,僅需在 pipeline 中加入相應(yīng)的 ChannelHandler 即可。
- netty-transport
Netty 提供數(shù)據(jù)處理和傳輸?shù)暮诵哪K,提供了很多非常重要的接口,如 Bootstrap、Channel、ChannelHandler、EventLoop、EventLoopGroup、ChannelPipeline 等。其中 Bootstrap 負(fù)責(zé)客戶端或服務(wù)端的啟動(dòng)工作,包括創(chuàng)建、初始化 Channel 等;EventLoop 負(fù)責(zé)向注冊(cè)的 Channel 發(fā)起 I/O 讀寫操作;ChannelPipeline 負(fù)責(zé) ChannelHandler 的有序編排。
六、總結(jié)
從整體結(jié)構(gòu)、邏輯架構(gòu)以及源碼結(jié)構(gòu)對(duì) Netty 的整體架構(gòu)進(jìn)行了初步介紹,可見 Netty 的分層架構(gòu)設(shè)計(jì)非常合理,實(shí)現(xiàn)了各層之間的邏輯解耦,對(duì)于開發(fā)者來說,只需要擴(kuò)展業(yè)務(wù)邏輯即可。
參考文章1
參考文章2
總結(jié)
以上是生活随笔為你收集整理的一、把握 Netty 整体架构脉络的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苏杰专访:产品创新好方向=几十年不变的需
- 下一篇: B端产品设计——批量导入