netty原理

Netty簡介

? ? ? ?Netty是一個高性能、異步事件驅動的NIO框架,基于JAVA NIO提供的API實現。它提供了對TCP、UDP文件傳輸的支持,作為一個異步NIO框架,Netty的所有IO操作都是異步非阻塞的,通過Future-Listener機制,用戶可以方便的主動獲取或者通過通知機制獲得IO操作結果。 作為當前最流行的NIO框架,Netty在互聯網領域、大數據分布式計算領域、游戲行業(yè)、通信行業(yè)等獲得了廣泛的應用,一些業(yè)界著名的開源組件也基于Netty的NIO框架構建。

netty原理分析

NIO通訊服務端步驟:

1、創(chuàng)建ServerSocketChannel,為它配置非阻塞模式

2、綁定監(jiān)聽,配置TCP參數,錄入backlog大小等

3、創(chuàng)建一個獨立的IO線程,用于輪詢多路復用器Selector

4、創(chuàng)建Selector,將之前的ServerSocketChannel注冊到Selector上,并設置監(jiān)聽標識位SelectionKey.ACCEPT

5、啟動IO線程,在循環(huán)體中執(zhí)行Selector.select()方法,輪詢就緒的通道

6、當輪詢到處于就緒的通道時,需要進行判斷操作位,如果是ACCEPT狀態(tài),說明是新的客戶端介入,則調用accept方法接受新的客戶端。

7、設置新接入客戶端的一些參數,并將其通道繼續(xù)注冊到Selector之中。設置監(jiān)聽標識等

8、如果輪詢的通道操作位是READ,則進行讀取,構造Buffer對象等

9、更細節(jié)的還有數據沒發(fā)送完成繼續(xù)發(fā)送的問題

零拷貝技術原理

? ? ? “零拷貝”是指計算機操作的過程中,CPU不需要為數據在內存之間的拷貝消耗資源。而它通常是指計算機在網絡上發(fā)送文件時,不需要將文件內容拷貝到用戶空間(User Space)而直接在內核空間(Kernel Space)中傳輸到網絡的方式。Netty的“零拷貝”主要體現在三個方面:

? ? ? ? 1、Netty的接收和發(fā)送ByteBuffer采用DIRECT BUFFERS,使用堆外直接內存進行Socket讀寫,不需要進行字節(jié)緩沖區(qū)的二次拷貝。如果使用傳統(tǒng)的堆內存(HEAP BUFFERS)進行Socket讀寫,JVM會將堆內存Buffer拷貝一份到直接內存中,然后才寫入Socket中。相比于堆外直接內存,消息在發(fā)送過程中多了一次緩沖區(qū)的內存拷貝讀取直接從“堆外直接內存”,不像傳統(tǒng)的堆內存和直接內存拷貝,ByteBufAllocator 通過ioBuffer分配堆外內存

? ? ? ?2、Netty提供了組合Buffer對象,可以聚合多個ByteBuffer對象,用戶可以像操作一個Buffer那樣方便的對組合Buffer進行操作,避免了傳統(tǒng)通過內存拷貝的方式將幾個小Buffer合并成一個大的Buffer,Netty允許我們將多段數據合并為一整段虛擬數據供用戶使用,而過程中不需要對數據進行拷貝操作,組合Buffer對象,避免了內存拷貝,ChannelBuffer接口:Netty為需要傳輸的數據制定了統(tǒng)一的ChannelBuffer接口,使用getByte(int index)方法來實現隨機訪問,使用雙指針的方式實現順序訪問

? ? ? ?3、Netty主要實現了HeapChannelBuffer,ByteBufferBackedChannelBuffer,與Zero Copy直接相關的CompositeChannelBuffer類

? ? ? ? CompositeChannelBuffer類的作用是將多個ChannelBuffer組成一個虛擬的ChannelBuffer來進行操作為什么說是虛擬的呢,因為CompositeChannelBuffer并沒有將多個ChannelBuffer真正的組合起來,而只是保存了他們的引用,這樣就避免了數據的拷貝,實現了Zero Copy,內部實現。其中readerIndex既讀指針和writerIndex既寫指針是從AbstractChannelBuffer繼承而來的。components是一個ChannelBuffer的數組,他保存了組成這個虛擬Buffer的所有子Buffer。indices是一個int類型的數組,它保存的是各個Buffer的索引值。lastAccessedComponentId是一個int值,它記錄了最后一次訪問時的子Buffer ID。CompositeChannelBuffer實際上就是將一系列的Buffer通過數組保存起來,然后實現了ChannelBuffer?的接口,使得在上層看來,操作這些Buffer就像是操作一個單獨的Buffer一樣。

? ? ? ? ?Netty的文件傳輸采用了transferTo方法,它可以直接將文件緩沖區(qū)的數據發(fā)送到目標Channel,避免了傳統(tǒng)通過循環(huán)write方式導致的內存拷貝問題。Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都實現了零拷貝的功能,而在Netty中也通過在FileRegion中包裝了NIO的FileChannel.transferTo()方法實現了零拷貝

Netty 的 Zero-copy 體現在如下幾個個方面:

1、Netty 提供了 CompositeByteBuf 類, 它可以將多個 ByteBuf 合并為一個邏輯上的 ByteBuf, 避免了各個 ByteBuf 之間的拷貝。

2、通過 wrap 操作, 我們可以將byte[] 數組、ByteBuf、ByteBuffer等包裝成一個 Netty ByteBuf 對象, 進而避免了拷貝操作。

3、ByteBuf 支持 slice 操作,因此可以將 ByteBuf 分解為多個共享同一個存儲區(qū)域的ByteBuf, 避免了內存的拷貝。

4、通過 FileRegion 包裝的FileChannel.tranferTo 實現文件傳輸, 可以直接將文件緩沖區(qū)的數據發(fā)送到目標 Channel, 避免了傳統(tǒng)通過循環(huán) write 方式導致的內存拷貝問題。

Netty的三層網絡架構進行設計

第一層:Reactor通信調度層。該層的主要職責就是監(jiān)聽網絡的連接和讀寫操作,負責將網絡層的數據讀取到內存緩沖區(qū)中,然后觸發(fā)各種網絡事件,例如連接創(chuàng)建、連接激活、讀事件、寫事件等,將這些事件觸發(fā)到Pipeline中,再由Pipeline充當的職責鏈來進行后續(xù)的處理。

第二層:職責鏈Pipeline層。負責事件在職責鏈中有序的向前(后)傳播,同時負責動態(tài)的編排職責鏈。Pipeline可以選擇監(jiān)聽和處理自己關心的事件。

第三層:業(yè)務邏輯處理層,一般可分為兩類:a. 純粹的業(yè)務邏輯處理,例如日志、訂單處理。b. 應用層協(xié)議管理,例如HTTP(S)協(xié)議、FTP協(xié)議等。

? ? ? ? 影響網絡服務通信性能的主要因素有:網絡I/O模型、線程(進程)調度模型和數據序列化方式。在網絡I/O模型方面,Netty采用基于非阻塞I/O的實現,底層依賴的是JDKNIO框架的Selector。在線程調度模型方面,Netty采用Reactor線程模型。

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

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

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

c、主從Reactor多線程模型:主從Reactor線程模型的特點是服務端用于接收客戶端連接的不再是一個單獨的NIO線程,而是一個獨立的NIO線程池。利用主從NIO線程模型,可以解決一個服務端監(jiān)聽線程無法有效處理所有客戶端連接的性能不足問題。Netty線程模型并非固定不變的,它可以支持三種Reactor線程模型。

在數據序列化方面,影響序列化性能的主要因素有:

a、序列化后的碼流大?。ňW絡帶寬占用)。

b、序列化和反序列化操作的性能(CPU資源占用)。

c、并發(fā)調用時的性能表現:穩(wěn)定性、線性增長等。

? ? ? ? Netty默認提供了對GoogleProtobuf二進制序列化框架的支持,但通過擴展Netty的編解碼接口,可以實現其它的高性能序列化框架,例如Avro、Thrift的壓縮二進制編解碼框架。

netty 為啥要進行拆包粘包處理

  簡單點描述,netty底層通訊是走的TCP協(xié)議,接收到的都是字節(jié)流,然后以字節(jié)字節(jié)隊列的形式存在緩存堆里面。而TCP協(xié)議每一次最大接收的字節(jié)長度是1024個字節(jié),一旦超過這個長度,那么就會出現一下各種形式

? ? ? ? 所以在字節(jié)長度超過1024的時候,一個完整的包可能會被TCP拆分成多個包進行發(fā)送,也有可能把多個小的包封裝成一個大的數據包發(fā)送,這就是所謂的TCP粘包和拆包問題。

netty基于以上問題也提供了一些組件,比如:

1、固定長度的拆包器 FixedLengthFrameDecoder,每個應用層數據包的都拆分成都是固定長度的大小

2、行拆包器 LineBasedFrameDecoder,每個應用層數據包,都以換行符作為分隔符,進行分割拆分

3、分隔符拆包器 DelimiterBasedFrameDecoder,每個應用層數據包,都通過自定義的分隔符,進行分割拆分

4、基于數據包長度的拆包器 LengthFieldBasedFrameDecoder,將應用層數據包的長度,作為接收端應用層數據包的拆分依據。按照應用層數據包的大小,拆包。這個拆包器,有一個要求,就是應用層協(xié)議中包含數據包的長度

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

友情鏈接更多精彩內容