Netty原理分析往這邊看!

Netty原理分析往這邊看!

 

Netty是一個高性能、異步事件驅動的NIO框架,它提供了對TCP、UDP和文件傳輸的支持,做爲一個異步NIO框架,Netty的全部IO操做都是異步非阻塞的,經過Future-Listener機制,用戶能夠方便的主動獲取或者經過通知機制得到IO操做結果。數組

做爲當前最流行的NIO框架,Netty在互聯網領域、大數據分佈式計算領域、遊戲行業、通訊行業等得到了普遍的應用,一些業界著名的開源組件也基於Netty的NIO框架構建。性能優化

Netty架構分析網絡

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線程上面完成。對於一些小容量應用場景,可使用單線程模型。

Netty原理分析往這邊看!

 

2.Reactor多線程模型:Rector多線程模型與單線程模型最大的區別就是有一組NIO線程處理I/O操做。主要用於高併發、大業務量場景。

Netty原理分析往這邊看!

 

3.主從Reactor多線程模型:主從Reactor線程模型的特色是服務端用於接收客戶端鏈接的再也不是個1個單獨的NIO線程,而是一個獨立的NIO線程池。利用主從NIO線程模型,能夠解決1個服務端監聽線程沒法有效處理全部客戶端鏈接的性能不足問題。

Netty原理分析往這邊看!

 

序列化方式

影響序列化性能的關鍵因素總結以下:

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 方式致使的內存拷貝問題。

相關文章
相關標籤/搜索