您好,登錄后才能下訂單哦!
Netty是一個高性能、異步事件驅動的NIO框架,它提供了對TCP、UDP和文件傳輸的支持,作為一個異步NIO框架,Netty的所有IO操作都是異步非阻塞的,通過Future-Listener機制,用戶可以方便的主動獲取或者通過通知機制獲得IO操作結果。
作為當前最流行的NIO框架,Netty在互聯網領域、大數據分布式計算領域、游戲行業、通信行業等獲得了廣泛的應用,一些業界著名的開源組件也基于Netty的NIO框架構建。
Netty架構分析
Netty 采用了比較典型的三層網絡架構進行設計,邏輯架構圖如下所示:
第一層:Reactor 通信調度層,它由一系列輔助類完成,包括 Reactor 線程 NioEventLoop 以及其父類、NioSocketChannel/NioServerSocketChannel 以及其父類、ByteBuffer 以及由其衍生出來的各種 Buffer、Unsafe 以及其衍生出的各種內部類等。該層的主要職責就是監聽網絡的讀寫和連接操作,負責將網絡層的數據讀取到內存緩沖區中,然后觸發各種網絡事件,例如連接創建、連接激活、讀事件、寫事件等等,將這些事件觸發到 PipeLine 中,由 PipeLine 充當的職責鏈來進行后續的處理。
第二層:職責鏈 PipeLine,它負責事件在職責鏈中的有序傳播,同時負責動態的編排職責鏈,職責鏈可以選擇監聽和處理自己關心的事件,它可以攔截處理和向后/向前傳播事件,不同的應用的 Handler 節點的功能也不同,通常情況下,往往會開發編解碼 Hanlder 用于消息的編解碼,它可以將外部的協議消息轉換成內部的 POJO 對象,這樣上層業務側只需要關心處理業務邏輯即可,不需要感知底層的協議差異和線程模型差異,實現了架構層面的分層隔離。
第三層:業務邏輯處理層,可以分為兩類:
1.純粹的業務邏輯處理,例如訂單處理。
2.應用層協議管理,例如HTTP協議、FTP協議等。
接下來,我從影響通信性能的三個方面(I/O模型、線程調度模型、序列化方式)來談談Netty的架構。
這里推薦一下我的Java架構學習群:479499375 ,群里有(Java高架構、分布式架構、高可擴展、高性能、高并發、性能優化、Spring boot、Redis、ActiveMQ、等學習資源)進群免費送給每一位Java小伙伴,不管你是轉行,還是工作中想提升自己能力都可以!
IO模型
Netty的I/O模型基于非阻塞I/O實現,底層依賴的是JDK NIO框架的Selector。
Selector提供選擇已經就緒的任務的能力。簡單來講,Selector會不斷地輪詢注冊在其上的Channel,如果某個Channel上面有新的TCP連接接入、讀和寫事件,這個Channel就處于就緒狀態,會被Selector輪詢出來,然后通過SelectionKey可以獲取就緒Channel的集合,進行后續的I/O操作。
線程調度模型
常用的Reactor線程模型有三種,分別如下:
1.Reactor單線程模型:Reactor單線程模型,指的是所有的I/O操作都在同一個NIO線程上面完成。對于一些小容量應用場景,可以使用單線程模型。
2.Reactor多線程模型:Rector多線程模型與單線程模型最大的區別就是有一組NIO線程處理I/O操作。主要用于高并發、大業務量場景。
3.主從Reactor多線程模型:主從Reactor線程模型的特點是服務端用于接收客戶端連接的不再是個1個單獨的NIO線程,而是一個獨立的NIO線程池。利用主從NIO線程模型,可以解決1個服務端監聽線程無法有效處理所有客戶端連接的性能不足問題。
序列化方式
影響序列化性能的關鍵因素總結如下:
1.序列化后的碼流大小(網絡帶寬占用)
2.序列化&反序列化的性能(CPU資源占用)
3.并發調用的性能表現:穩定性、線性增長、偶現的時延毛刺等
這里推薦一下我的Java架構學習群:479499375 ,群里有(Java高架構、分布式架構、高可擴展、高性能、高并發、性能優化、Spring boot、Redis、ActiveMQ、等學習資源)進群免費送給每一位Java小伙伴,不管你是轉行,還是工作中想提升自己能力都可以!
鏈路有效性檢測
心跳檢測機制分為三個層面:
1.TCP層面的心跳檢測,即TCP的Keep-Alive機制,它的作用域是整個TCP協議棧;
2.協議層的心跳檢測,主要存在于長連接協議中。例如SMPP協議;
3.應用層的心跳檢測,它主要由各業務產品通過約定方式定時給對方發送心跳消息實現。
心跳檢測的目的就是確認當前鏈路可用,對方活著并且能夠正常接收和發送消息。作為高可靠的NIO框架,Netty也提供了基于鏈路空閑的心跳檢測機制:
1.讀空閑,鏈路持續時間t沒有讀取到任何消息;
2.寫空閑,鏈路持續時間t沒有發送任何消息;
3.讀寫空閑,鏈路持續時間t沒有接收或者發送任何消息。
零拷貝
“零拷貝”是指計算機操作的過程中, CPU不需要為數據在內存之間的拷貝消耗資源 。而它通常是指計算機在網絡上發送文件時,不需要將文件內容拷貝到用戶空間(User Space)而 直接在內核空間(Kernel Space)中傳輸到網絡的方式
Netty的“零拷貝”主要體現在三個方面
Netty的 接收和發送ByteBuffer采用DIRECT BUFFERS,使用堆外直接內存進行Socket讀寫,不需要進行字節緩沖區的二次拷貝 。如果使用傳統的堆內存(HEAP BUFFERS)進行Socket讀寫,JVM會將堆內存Buffer拷貝一份到直接內存中,然后才寫入Socket中。相比于堆外直接內存,消息在發送過程中多了一次緩沖區的內存拷貝
讀取直接從“堆外直接內存”,不像傳統的堆內存和直接內存拷貝
ByteBufAllocator 通過ioBuffer分配堆外內存
Netty提供了 組合Buffer對象 ,可以聚合多個ByteBuffer對象,用戶可以 像操作一個Buffer那樣方便的對組合Buffer進行操作 ,避免了傳統通過內存拷貝的方式將幾個小Buffer合并成一個大的Buffer
Netty允許我們將多段數據合并為一整段虛擬數據供用戶使用,而過程中不需要對數據進行拷貝操作
組合Buffer對象,避免了內存拷貝
ChannelBuffer接口:Netty為需要傳輸的數據制定了統一的ChannelBuffer接口
· 使用getByte(int index)方法來實現隨機訪問
· 使用雙指針的方式實現順序訪問
· Netty主要實現了HeapChannelBuffer,ByteBufferBackedChannelBuffer,與Zero Copy直接相關的CompositeChannelBuffer類
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方法 ,它可以直接將文件緩沖區的數據發送到目標Channel,避免了傳統通過循環write方式導致的內存拷貝問題
Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都實現了零拷貝的功能,而在Netty中也通過在FileRegion中包裝了NIO的FileChannel.transferTo()方法實現了零拷貝
Netty 的 Zero-copy 體現在如下幾個個方面:
l Netty 提供了 CompositeByteBuf 類, 它可以將多個 ByteBuf 合并為一個邏輯上的 ByteBuf, 避免了各個 ByteBuf 之間的拷貝。
l 通過 wrap 操作, 我們可以將byte[] 數組、ByteBuf、ByteBuffer等包裝成一個 Netty ByteBuf 對象, 進而避免了拷貝操作。
l ByteBuf 支持 slice 操作,因此可以將 ByteBuf 分解為多個共享同一個存儲區域的ByteBuf, 避免了內存的拷貝。
l 通過 FileRegion 包裝的FileChannel.tranferTo 實現文件傳輸, 可以直接將文件緩沖區的數據發送到目標 Channel, 避免了傳統通過循環 write 方式導致的內存拷貝問題。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。