【51CTO.com原創(chuàng)稿件】在分布式系統(tǒng)被廣泛應(yīng)用的今天,服務(wù)有可能分布在網(wǎng)絡(luò)中的各個(gè)節(jié)點(diǎn)中。因此,服務(wù)之間的調(diào)用對(duì)分布式系統(tǒng)來(lái)說(shuō),就顯得尤為重要。
圖片來(lái)自 Pexels
對(duì)于高性能的 RPC 框架,Netty 作為異步通信框架,幾乎成為必備品。例如,Dubbo 框架中通信組件,還有 RocketMQ 中生產(chǎn)者和消費(fèi)者的通信,都使用了 Netty。今天,我們來(lái)看看 Netty 的基本架構(gòu)和原理。
Netty 的特點(diǎn)與 NIO
Netty 是一個(gè)異步的、基于事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架,它可以用來(lái)開(kāi)發(fā)高性能服務(wù)端和客戶端。
以前編寫(xiě)網(wǎng)絡(luò)調(diào)用程序的時(shí)候,我們都會(huì)在客戶端創(chuàng)建一個(gè) Socket,通過(guò)這個(gè) Socket 連接到服務(wù)端。
服務(wù)端根據(jù)這個(gè) Socket 創(chuàng)建一個(gè) Thread,用來(lái)發(fā)出請(qǐng)求。客戶端在發(fā)起調(diào)用以后,需要等待服務(wù)端處理完成,才能繼續(xù)后面的操作。這樣線程會(huì)出現(xiàn)等待的狀態(tài)。
如果客戶端請(qǐng)求數(shù)越多,服務(wù)端創(chuàng)建的處理線程也會(huì)越多,JVM 如此多的線程并不是一件容易的事。
使用阻賽 I/O 處理多個(gè)連接
為了解決上述的問(wèn)題,推出了 NIO 的概念,也就是(Non-blocking I/O)。其中,Selector 機(jī)制就是 NIO 的核心。
當(dāng)每次客戶端請(qǐng)求時(shí),會(huì)創(chuàng)建一個(gè) Socket Channel,并將其注冊(cè)到 Selector 上(多路復(fù)用器)。
然后,Selector 關(guān)注服務(wù)端 IO 讀寫(xiě)事件,此時(shí)客戶端并不用等待 IO 事件完成,可以繼續(xù)做接下來(lái)的工作。
一旦,服務(wù)端完成了 IO 讀寫(xiě)操作,Selector 會(huì)接到通知,同時(shí)告訴客戶端 IO 操作已經(jīng)完成。
接到通知的客戶端,就可以通過(guò) SocketChannel 獲取需要的數(shù)據(jù)了。
NIO 機(jī)制與 Selector
上面描述的過(guò)程有點(diǎn)異步的意思,不過(guò),Selector 實(shí)現(xiàn)的并不是真正意義上的異步操作。
因?yàn)?Selector 需要通過(guò)線程阻塞的方式監(jiān)聽(tīng) IO 事件變更,只是這種方式?jīng)]有讓客戶端等待,是 Selector 在等待 IO 返回,并且通知客戶端去獲取數(shù)據(jù)。真正“異步 IO”(AIO)這里不展開(kāi)介紹,有興趣可以自行查找。
說(shuō)好了 NIO 再來(lái)談?wù)?Netty,Netty 作為 NIO 的實(shí)現(xiàn),它適用于服務(wù)器/客戶端通訊的場(chǎng)景,以及針對(duì)于 TCP 協(xié)議下的高并發(fā)應(yīng)用。
對(duì)于開(kāi)發(fā)者來(lái)說(shuō),它具有以下特點(diǎn):
- 對(duì) NIO 進(jìn)行封裝,開(kāi)發(fā)者不需要關(guān)注 NIO 的底層原理,只需要調(diào)用 Netty 組件就能夠完成工作。
- 對(duì)網(wǎng)絡(luò)調(diào)用透明,從 Socket 建立 TCP 連接到網(wǎng)絡(luò)異常的處理都做了包裝。
- 對(duì)數(shù)據(jù)處理靈活, Netty 支持多種序列化框架,通過(guò)“ChannelHandler”機(jī)制,可以自定義“編/解碼器”。
- 對(duì)性能調(diào)優(yōu)友好,Netty 提供了線程池模式以及 Buffer 的重用機(jī)制(對(duì)象池化),不需要構(gòu)建復(fù)雜的多線程模型和操作隊(duì)列。
從一個(gè)簡(jiǎn)單的例子開(kāi)始
開(kāi)篇講到了,為了滿足高并發(fā)下網(wǎng)絡(luò)請(qǐng)求,引入了 NIO 的概念。Netty 是針對(duì) NIO 的實(shí)現(xiàn),在 NIO 封裝,網(wǎng)絡(luò)調(diào)用,數(shù)據(jù)處理以及性能優(yōu)化等方面都有不俗的表現(xiàn)。
學(xué)習(xí)架構(gòu)最容易的方式就是從實(shí)例入手,從客戶端訪問(wèn)服務(wù)端的代碼來(lái)看看 Netty 是如何運(yùn)作的。再一次介紹代碼中調(diào)用的組件以及組件的工作原理。
假設(shè)有一個(gè)客戶端去調(diào)用一個(gè)服務(wù)端,假設(shè)服務(wù)端叫做 EchoServer,客戶端叫做 EchoClient,用 Netty 架構(gòu)實(shí)現(xiàn)代碼如下。
服務(wù)端代碼
構(gòu)建服務(wù)器端,假設(shè)服務(wù)器接受客戶端傳來(lái)的信息,然后在控制臺(tái)打印。首先,生成 EchoServer,在構(gòu)造函數(shù)中傳入需要監(jiān)聽(tīng)的端口號(hào)。
構(gòu)造函數(shù)中傳入需要監(jiān)聽(tīng)的端口號(hào)
接下來(lái)就是服務(wù)的啟動(dòng)方法:
啟動(dòng) NettyServer 的 Start 方法
Server 的啟動(dòng)方法涉及到了一些組件的調(diào)用,例如 EventLoopGroup,Channel。這些會(huì)在后面詳細(xì)講解。
這里有個(gè)大致的印象就好:
- 創(chuàng)建 EventLoopGroup。
- 創(chuàng)建 ServerBootstrap。
- 指定所使用的 NIO 傳輸 Channel。
- 使用指定的端口設(shè)置套接字地址。
- 添加一個(gè) ServerHandler 到 Channel 的 ChannelPipeline。
- 異步地綁定服務(wù)器;調(diào)用 sync() 方法阻塞等待直到綁定完成。
- 獲取 Channel 的 CloseFuture,并且阻塞當(dāng)前線程直到它完成。
- 關(guān)閉 EventLoopGroup,釋放所有的資源。
NettyServer 啟動(dòng)以后會(huì)監(jiān)聽(tīng)某個(gè)端口的請(qǐng)求,當(dāng)接受到了請(qǐng)求就需要處理了。在 Netty 中客戶端請(qǐng)求服務(wù)端,被稱為“入站”操作。
可以通過(guò) ChannelInboundHandlerAdapter 實(shí)現(xiàn),具體內(nèi)容如下:
處理來(lái)自客戶端的請(qǐng)求
從上面的代碼可以看出,服務(wù)端處理的代碼包含了三個(gè)方法。這三個(gè)方法都是根據(jù)事件觸發(fā)的。
他們分別是:
- 當(dāng)接收到消息時(shí)的操作,channelRead。
- 消息讀取完成時(shí)的方法,channelReadComplete。
- 出現(xiàn)異常時(shí)的方法,exceptionCaught。
客戶端代碼
客戶端和服務(wù)端的代碼基本相似,在初始化時(shí)需要輸入服務(wù)端的 IP 和 Port。
同樣在客戶端啟動(dòng)函數(shù)中包括以下內(nèi)容:
客戶端啟動(dòng)程序的順序:
- 創(chuàng)建 Bootstrap。
- 指定 EventLoopGroup 用來(lái)監(jiān)聽(tīng)事件。
- 定義 Channel 的傳輸模式為 NIO(Non-BlockingInputOutput)。
- 設(shè)置服務(wù)器的 InetSocketAddress。
- 在創(chuàng)建 Channel 時(shí),向 ChannelPipeline 中添加一個(gè) EchoClientHandler 實(shí)例。
- 連接到遠(yuǎn)程節(jié)點(diǎn),阻塞等待直到連接完成。
- 阻塞,直到 Channel 關(guān)閉。
- 關(guān)閉線程池并且釋放所有的資源。
客戶端在完成以上操作以后,會(huì)與服務(wù)端建立連接從而傳輸數(shù)據(jù)。同樣在接受到 Channel 中觸發(fā)的事件時(shí),客戶端會(huì)觸發(fā)對(duì)應(yīng)事件的操作。
例如 Channel 激活,客戶端接受到服務(wù)端的消息,或者發(fā)生異常的捕獲。
從代碼結(jié)構(gòu)上看還是比較簡(jiǎn)單的。服務(wù)端和客戶端分別初始化創(chuàng)建監(jiān)聽(tīng)和連接。然后分別定義各自的 Handler 處理對(duì)方的請(qǐng)求。
服務(wù)端/客戶端初始化和事件處理
Netty 核心組件
通過(guò)上面的簡(jiǎn)單例子,發(fā)現(xiàn)有些 Netty 組件在服務(wù)初始化以及通訊時(shí)被用到,下面就來(lái)介紹一下這些組件的用途和關(guān)系。
①Channel
通過(guò)上面例子可以看出,當(dāng)客戶端和服務(wù)端連接的時(shí)候會(huì)建立一個(gè) Channel。
這個(gè) Channel 我們可以理解為 Socket 連接,它負(fù)責(zé)基本的 IO 操作,例如:bind(),connect(),read(),write() 等等。
簡(jiǎn)單的說(shuō),Channel 就是代表連接,實(shí)體之間的連接,程序之間的連接,文件之間的連接,設(shè)備之間的連接。同時(shí)它也是數(shù)據(jù)入站和出站的載體。
②EventLoop 和 EventLoopGroup
既然有了 Channel 連接服務(wù),讓信息之間可以流動(dòng)。如果服務(wù)發(fā)出的消息稱作“出站”消息,服務(wù)接受的消息稱作“入站”消息。那么消息的“出站”/“入站”就會(huì)產(chǎn)生事件(Event)。
例如:連接已激活;數(shù)據(jù)讀取;用戶事件;異常事件;打開(kāi)鏈接;關(guān)閉鏈接等等。
順著這個(gè)思路往下想,有了數(shù)據(jù),數(shù)據(jù)的流動(dòng)產(chǎn)生事件,那么就有一個(gè)機(jī)制去監(jiān)控和協(xié)調(diào)事件。
這個(gè)機(jī)制(組件)就是 EventLoop。在 Netty 中每個(gè) Channel 都會(huì)被分配到一個(gè) EventLoop。一個(gè) EventLoop 可以服務(wù)于多個(gè) Channel。
每個(gè) EventLoop 會(huì)占用一個(gè) Thread,同時(shí)這個(gè) Thread 會(huì)處理 EventLoop 上面發(fā)生的所有 IO 操作和事件(Netty 4.0)。
EventLoop 與 Channel 關(guān)系
理解了 EventLoop,再來(lái)說(shuō) EventLoopGroup 就容易了,EventLoopGroup 是用來(lái)生成 EventLoop 的,還記得例子代碼中第一行就 new 了 EventLoopGroup 對(duì)象。
一個(gè) EventLoopGroup 中包含了多個(gè) EventLoop 對(duì)象。
創(chuàng)建 EventLoopGroup
EventLoopGroup 要做的就是創(chuàng)建一個(gè)新的 Channel,并且給它分配一個(gè) EventLoop。
EventLoopGroup,EventLoop 和 Channel 的關(guān)系
在異步傳輸?shù)那闆r下,一個(gè) EventLoop 是可以處理多個(gè) Channel 中產(chǎn)生的事件的,它主要的工作就是事件的發(fā)現(xiàn)以及通知。
相對(duì)于以前一個(gè) Channel 就占用一個(gè) Thread 的情況。Netty 的方式就要合理多了。
客戶端發(fā)送消息到服務(wù)端,EventLoop 發(fā)現(xiàn)以后會(huì)告訴服務(wù)端:“你去獲取消息”,同時(shí)客戶端進(jìn)行其他的工作。
當(dāng) EventLoop 檢測(cè)到服務(wù)端返回的消息,也會(huì)通知客戶端:“消息返回了,你去取吧“。客戶端再去獲取消息。整個(gè)過(guò)程 EventLoop 就是監(jiān)視器+傳聲筒。
③ChannelHandler,ChannelPipeline 和 ChannelHandlerContext
如果說(shuō) EventLoop 是事件的通知者,那么 ChannelHandler 就是事件的處理者。
在 ChannelHandler 中可以添加一些業(yè)務(wù)代碼,例如數(shù)據(jù)轉(zhuǎn)換,邏輯運(yùn)算等等。
正如上面例子中展示的,Server 和 Client 分別都有一個(gè) ChannelHandler 來(lái)處理,讀取信息,網(wǎng)絡(luò)可用,網(wǎng)絡(luò)異常之類的信息。
并且,針對(duì)出站和入站的事件,有不同的 ChannelHandler,分別是:
- ChannelInBoundHandler(入站事件處理器)
- ChannelOutBoundHandler(出站事件處理器)
假設(shè)每次請(qǐng)求都會(huì)觸發(fā)事件,而由 ChannelHandler 來(lái)處理這些事件,這個(gè)事件的處理順序是由 ChannelPipeline 來(lái)決定的。
ChannelHanlder 處理,出站/入站的事件
ChannelPipeline 為 ChannelHandler 鏈提供了容器。到 Channel 被創(chuàng)建的時(shí)候,會(huì)被 Netty 框架自動(dòng)分配到 ChannelPipeline 上。
ChannelPipeline 保證 ChannelHandler 按照一定順序處理事件,當(dāng)事件觸發(fā)以后,會(huì)將數(shù)據(jù)通過(guò) ChannelPipeline 按照一定的順序通過(guò) ChannelHandler。
說(shuō)白了,ChannelPipeline 是負(fù)責(zé)“排隊(duì)”的。這里的“排隊(duì)”是處理事件的順序。
同時(shí),ChannelPipeline 也可以添加或者刪除 ChannelHandler,管理整個(gè)隊(duì)列。
如上圖,ChannelPipeline 使 ChannelHandler 按照先后順序排列,信息按照箭頭所示方向流動(dòng)并且被 ChannelHandler 處理。
說(shuō)完了 ChannelPipeline 和 ChannelHandler,前者管理后者的排列順序。那么它們之間的關(guān)聯(lián)就由 ChannelHandlerContext 來(lái)表示了。
每當(dāng)有 ChannelHandler 添加到 ChannelPipeline 時(shí),同時(shí)會(huì)創(chuàng)建 ChannelHandlerContext 。
ChannelHandlerContext 的主要功能是管理 ChannelHandler 和 ChannelPipeline 的交互。
不知道大家注意到?jīng)]有,開(kāi)始的例子中 ChannelHandler 中處理事件函數(shù),傳入的參數(shù)就是 ChannelHandlerContext。
ChannelHandlerContext 參數(shù)貫穿 ChannelPipeline,將信息傳遞給每個(gè) ChannelHandler,是個(gè)合格的“通訊員”。
ChannelHandlerContext 負(fù)責(zé)傳遞消息
把上面提到的幾個(gè)核心組件歸納一下,用下圖表示方便記憶他們之間的關(guān)系。
Netty 核心組件關(guān)系圖
Netty 的數(shù)據(jù)容器
前面介紹了 Netty 的幾個(gè)核心組件,服務(wù)器在數(shù)據(jù)傳輸?shù)臅r(shí)候,產(chǎn)生事件,并且對(duì)事件進(jìn)行監(jiān)控和處理。
接下來(lái)看看數(shù)據(jù)是如何存放以及是如何讀寫(xiě)的。Netty 將 ByteBuf 作為數(shù)據(jù)容器,來(lái)存放數(shù)據(jù)。
ByteBuf 工作原理
從結(jié)構(gòu)上來(lái)說(shuō),ByteBuf 由一串字節(jié)數(shù)組構(gòu)成。數(shù)組中每個(gè)字節(jié)用來(lái)存放信息。
ByteBuf 提供了兩個(gè)索引,一個(gè)用于讀取數(shù)據(jù),一個(gè)用于寫(xiě)入數(shù)據(jù)。這兩個(gè)索引通過(guò)在字節(jié)數(shù)組中移動(dòng),來(lái)定位需要讀或者寫(xiě)信息的位置。
當(dāng)從 ByteBuf 讀取時(shí),它的 readerIndex(讀索引)將會(huì)根據(jù)讀取的字節(jié)數(shù)遞增。
同樣,當(dāng)寫(xiě) ByteBuf 時(shí),它的 writerIndex 也會(huì)根據(jù)寫(xiě)入的字節(jié)數(shù)進(jìn)行遞增。
ByteBuf 讀寫(xiě)索引圖例
需要注意的是極限的情況是 readerIndex 剛好讀到了 writerIndex 寫(xiě)入的地方。
如果 readerIndex 超過(guò)了 writerIndex 的時(shí)候,Netty 會(huì)拋出 IndexOutOf-BoundsException 異常。
ByteBuf 使用模式
談了 ByteBuf 的工作原理以后,再來(lái)看看它的使用模式。
根據(jù)存放緩沖區(qū)的不同分為三類:
- 堆緩沖區(qū),ByteBuf 將數(shù)據(jù)存儲(chǔ)在 JVM 的堆中,通過(guò)數(shù)組實(shí)現(xiàn),可以做到快速分配。
由于在堆上被 JVM 管理,在不被使用時(shí)可以快速釋放。可以通過(guò) ByteBuf.array() 來(lái)獲取 byte[] 數(shù)據(jù)。
- 直接緩沖區(qū),在 JVM 的堆之外直接分配內(nèi)存,用來(lá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ù)分配在堆之外,通過(guò) JVM 進(jìn)行垃圾回收,并且分配時(shí)也需要做復(fù)制的操作,因此使用成本較高。
- 復(fù)合緩沖區(qū),顧名思義就是將上述兩類緩沖區(qū)聚合在一起。Netty 提供了一個(gè) CompsiteByteBuf,可以將堆緩沖區(qū)和直接緩沖區(qū)的數(shù)據(jù)放在一起,讓使用更加方便。
ByteBuf 的分配
聊完了結(jié)構(gòu)和使用模式,再來(lái)看看 ByteBuf 是如何分配緩沖區(qū)的數(shù)據(jù)的。
Netty 提供了兩種 ByteBufAllocator 的實(shí)現(xiàn),他們分別是:
- PooledByteBufAllocator,實(shí)現(xiàn)了 ByteBuf 的對(duì)象的池化,提高性能減少內(nèi)存碎片。
- Unpooled-ByteBufAllocator,沒(méi)有實(shí)現(xiàn)對(duì)象的池化,每次會(huì)生成新的對(duì)象實(shí)例。
對(duì)象池化的技術(shù)和線程池,比較相似,主要目的是提高內(nèi)存的使用率。池化的簡(jiǎn)單實(shí)現(xiàn)思路,是在 JVM 堆內(nèi)存上構(gòu)建一層內(nèi)存池,通過(guò) allocate 方法獲取內(nèi)存池中的空間,通過(guò) release 方法將空間歸還給內(nèi)存池。
對(duì)象的生成和銷毀,會(huì)大量地調(diào)用 allocate 和 release 方法,因此內(nèi)存池面臨碎片空間回收的問(wèn)題,在頻繁申請(qǐng)和釋放空間后,內(nèi)存池需要保證連續(xù)的內(nèi)存空間,用于對(duì)象的分配。
基于這個(gè)需求,有兩種算法用于優(yōu)化這一塊的內(nèi)存分配:伙伴系統(tǒng)和 slab 系統(tǒng)。
伙伴系統(tǒng),用完全二叉樹(shù)管理內(nèi)存區(qū)域,左右節(jié)點(diǎn)互為伙伴,每個(gè)節(jié)點(diǎn)代表一個(gè)內(nèi)存塊。內(nèi)存分配將大塊內(nèi)存不斷二分,直到找到滿足所需的最小內(nèi)存分片。
內(nèi)存釋放會(huì)判斷釋放內(nèi)存分片的伙伴(左右節(jié)點(diǎn))是否空閑,如果空閑則將左右節(jié)點(diǎn)合成更大塊內(nèi)存。
slab 系統(tǒng),主要解決內(nèi)存碎片問(wèn)題,將大塊內(nèi)存按照一定內(nèi)存大小進(jìn)行等分,形成相等大小的內(nèi)存片構(gòu)成的內(nèi)存集。
按照內(nèi)存申請(qǐng)空間的大小,申請(qǐng)盡量小塊內(nèi)存或者其整數(shù)倍的內(nèi)存,釋放內(nèi)存時(shí),也是將內(nèi)存分片歸還給內(nèi)存集。
Netty 內(nèi)存池管理以 Allocate 對(duì)象的形式出現(xiàn)。一個(gè) Allocate 對(duì)象由多個(gè) Arena 組成,每個(gè) Arena 能執(zhí)行內(nèi)存塊的分配和回收。
Arena 內(nèi)有三類內(nèi)存塊管理單元:
- TinySubPage
- SmallSubPage
- ChunkList
Tiny 和 Small 符合 Slab 系統(tǒng)的管理策略,ChunkList 符合伙伴系統(tǒng)的管理策略。
當(dāng)用戶申請(qǐng)內(nèi)存介于 tinySize 和 smallSize 之間時(shí),從 tinySubPage 中獲取內(nèi)存塊。
申請(qǐng)內(nèi)存介于 smallSize 和 pageSize 之間時(shí),從 smallSubPage 中獲取內(nèi)存塊;介于 pageSize 和 chunkSize 之間時(shí),從 ChunkList 中獲取內(nèi)存;大于 ChunkSize(不知道分配內(nèi)存的大小)的內(nèi)存塊不通過(guò)池化分配。
Netty 的 Bootstrap
說(shuō)完了 Netty 的核心組件以及數(shù)據(jù)存儲(chǔ)。再回到最開(kāi)始的例子程序,在程序最開(kāi)始的時(shí)候會(huì) new 一個(gè) Bootstrap 對(duì)象,后面所有的配置都是基于這個(gè)對(duì)象展開(kāi)的。
生成 Bootstrap 對(duì)象
Bootstrap 的作用就是將 Netty 核心組件配置到程序中,并且讓他們運(yùn)行起來(lái)。
從 Bootstrap 的繼承結(jié)構(gòu)來(lái)看,分為兩類分別是 Bootstrap 和 ServerBootstrap,一個(gè)對(duì)應(yīng)客戶端的引導(dǎo),另一個(gè)對(duì)應(yīng)服務(wù)端的引導(dǎo)。
支持客戶端和服務(wù)端的程序引導(dǎo)
客戶端引導(dǎo) Bootstrap,主要有兩個(gè)方法 bind() 和 connect()。Bootstrap 通過(guò) bind() 方法創(chuàng)建一個(gè) Channel。
在 bind() 之后,通過(guò)調(diào)用 connect() 方法來(lái)創(chuàng)建 Channel 連接。
Bootstrap 通過(guò) bind 和 connect 方法創(chuàng)建連接
服務(wù)端引導(dǎo) ServerBootstrap,與客戶端不同的是在 Bind() 方法之后會(huì)創(chuàng)建一個(gè) ServerChannel,它不僅會(huì)創(chuàng)建新的 Channel 還會(huì)管理已經(jīng)存在的 Channel。
ServerBootstrap 通過(guò) bind 方法創(chuàng)建/管理連接
通過(guò)上面的描述,服務(wù)端和客戶端的引導(dǎo)存在兩個(gè)區(qū)別:
- ServerBootstrap(服務(wù)端引導(dǎo))綁定一個(gè)端口,用來(lái)監(jiān)聽(tīng)客戶端的連接請(qǐng)求。而 Bootstrap(客戶端引導(dǎo))只要知道服務(wù)端 IP 和 Port 建立連接就可以了。
- Bootstrap(客戶端引導(dǎo))需要一個(gè) EventLoopGroup,但是 ServerBootstrap(服務(wù)端引導(dǎo))則需要兩個(gè) EventLoopGroup。
因?yàn)榉?wù)器需要兩組不同的 Channel。第一組 ServerChannel 自身監(jiān)聽(tīng)本地端口的套接字。第二組用來(lái)監(jiān)聽(tīng)客戶端請(qǐng)求的套接字。
ServerBootstrap 有兩組 EventLoopGroup
總結(jié)
我們從 NIO 入手,談到了 Selector 的核心機(jī)制。然后通過(guò)介紹 Netty 客戶端和服務(wù)端源代碼運(yùn)行流程,讓大家對(duì) Netty 編寫(xiě)代碼有基本的認(rèn)識(shí)。
在 Netty 的核心組件中,Channel 提供 Socket 的連接通道,EventLoop 會(huì)對(duì)應(yīng) Channel 監(jiān)聽(tīng)其產(chǎn)生的事件,并且通知執(zhí)行者。EventloopGroup 的容器,負(fù)責(zé)生成和管理 EventLoop。
ChannelPipeline 作為 ChannelHandler 的容器會(huì)綁定到 Channel 上,然后由 ChannelHandler 提供具體事件處理。另外,ChannelHandlerContext 為 ChannelHandler 和 ChannelPipeline 提供信息共享。
ByteBuf 作為 Netty 的數(shù)據(jù)容器,通過(guò)字節(jié)數(shù)組的方式存儲(chǔ)數(shù)據(jù),并且通過(guò)讀索引和寫(xiě)索引來(lái)引導(dǎo)讀寫(xiě)操作。
上述的核心組件都是通過(guò) Bootstrap 來(lái)配置并且引導(dǎo)啟動(dòng)的,Bootstrap 啟動(dòng)方式雖然一致,但是針對(duì)客戶端和服務(wù)端有些許的區(qū)別。
作者:崔皓
簡(jiǎn)介:十六年開(kāi)發(fā)和架構(gòu)經(jīng)驗(yàn),曾擔(dān)任過(guò)惠普武漢交付中心技術(shù)專家,需求分析師,項(xiàng)目經(jīng)理,后在創(chuàng)業(yè)公司擔(dān)任技術(shù)/產(chǎn)品經(jīng)理。善于學(xué)習(xí),樂(lè)于分享。目前專注于技術(shù)架構(gòu)與研發(fā)管理。
【51CTO原創(chuàng)稿件,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文作者和出處為51CTO.com】