那些與Netty有關的知識點,你知道多少

Netty 原理
Netty 是一個高性能、異步事件驅動的 NIO 框架,基於 JAVA NIO 提供的 API 實現。它提供了對TCP、UDP 和文件傳輸的支持,作爲一個異步 NIO 框架,Netty 的所有 IO 操作都是異步非阻塞的,通過 Future-Listener 機制,用戶可以方便的主動獲取或者通過通知機制獲得 IO 操作結果。

Netty 高性能
在 IO 編程過程中,當需要同時處理多個客戶端接入請求時,可以利用多線程或者 IO 多路複用技術進行處理。IO 多路複用技術通過把多個 IO 的阻塞複用到同一個 select 的阻塞上,從而使得系統在單線程的情況下可以同時處理多個客戶端請求。與傳統的多線程/多進程模型比,I/O 多路複用的最大優勢是系統開銷小,系統不需要創建新的額外進程或者線程,也不需要維護這些進程和線程的運行,降低了系統的維護工作量,節省了系統資源。

與 Socket 類和 ServerSocket 類相對應,NIO 也提供了 SocketChannel 和 ServerSocketChannel兩種不同的套接字通道實現。

多路複用通訊方式

Netty 架構按照 Reactor 模式設計和實現,它的服務端通信序列圖如下:

那些與Netty有關的知識點,你知道多少
客戶端通信序列圖如下:
那些與Netty有關的知識點,你知道多少
Netty 的 IO 線程 NioEventLoop 由於聚合了多路複用器 Selector,可以同時併發處理成百上千個客戶端 Channel,由於讀寫操作都是非阻塞的,這就可以充分提升 IO 線程的運行效率,避免由於頻繁 IO 阻塞導致的線程掛起。

異步通訊 NIO

由於 Netty 採用了異步通信模式,一個 IO 線程可以併發處理 N 個客戶端連接和讀寫操作,這從根本上解決了傳統同步阻塞 IO 一連接一線程模型,架構的性能、彈性伸縮能力和可靠性都得到了極大的提升。

零拷貝(DIRECT BUFFERS 使用堆外直接內存)

  1. Netty 的接收和發送 ByteBuffer 採用 DIRECT BUFFERS,使用堆外直接內存進行 Socket 讀寫,不需要進行字節緩衝區的二次拷貝。如果使用傳統的堆內存(HEAP BUFFERS)進行 Socket 讀寫,JVM 會將堆內存 Buffer 拷貝一份到直接內存中,然後才寫入 Socket 中。相比於堆外直接內存,消息在發送過程中多了一次緩衝區的內存拷貝。

  2. Netty 提供了組合 Buffer 對象,可以聚合多個 ByteBuffer 對象,用戶可以像操作一個 Buffer 那樣方便的對組合 Buffer 進行操作,避免了傳統通過內存拷貝的方式將幾個小 Buffer 合併成一個大的Buffer。

  3. Netty的文件傳輸採用了transferTo方法,它可以直接將文件緩衝區的數據發送到目標Channel,避免了傳統通過循環 write 方式導致的內存拷貝問題內存池(基於內存池的緩衝區重用機制)隨着 JVM 虛擬機和 JIT 即時編譯技術的發展,對象的分配和回收是個非常輕量級的工作。但是對於緩衝區 Buffer,情況卻稍有不同,特別是對於堆外直接內存的分配和回收,是一件耗時的操作。爲了儘量重用緩衝區,Netty 提供了基於內存池的緩衝區重用機制。

高效的 Reactor 線程模型

常用的 Reactor 線程模型有三種,Reactor 單線程模型, Reactor 多線程模型, 主從 Reactor 多線程模型。

Reactor 單線程模型

Reactor 單線程模型,指的是所有的 IO 操作都在同一個 NIO 線程上面完成,NIO 線程的職責如下:

1) 作爲 NIO 服務端,接收客戶端的 TCP 連接;

2) 作爲 NIO 客戶端,向服務端發起 TCP 連接;

3) 讀取通信對端的請求或者應答消息;

4) 向通信對端發送消息請求或者應答消息。

那些與Netty有關的知識點,你知道多少

由於 Reactor 模式使用的是異步非阻塞 IO,所有的 IO 操作都不會導致阻塞,理論上一個線程可以獨立處理所有 IO 相關的操作。從架構層面看,一個 NIO 線程確實可以完成其承擔的職責。例如,通過Acceptor 接收客戶端的 TCP 連接請求消息,鏈路建立成功之後,通過 Dispatch 將對應的 ByteBuffer派發到指定的 Handler 上進行消息解碼。用戶 Handler 可以通過 NIO 線程將消息發送給客戶端。

Reactor 多線程模型

Rector 多線程模型與單線程模型最大的區別就是有一組 NIO 線程處理 IO 操作。 有專門一個NIO 線程-Acceptor 線程用於監聽服務端,接收客戶端的 TCP 連接請求; 網絡 IO 操作-讀、寫等由一個 NIO 線程池負責,線程池可以採用標準的 JDK 線程池實現,它包含一個任務隊列和 N個可用的線程,由這些 NIO 線程負責消息的讀取、解碼、編碼和發送;

那些與Netty有關的知識點,你知道多少
主從 Reactor 多線程模型

服務端用於接收客戶端連接的不再是個 1 個單獨的 NIO 線程,而是一個獨立的 NIO 線程池。Acceptor 接收到客戶端 TCP 連接請求處理完成後(可能包含接入認證等),將新創建的SocketChannel 註冊到 IO 線程池(sub reactor 線程池)的某個 IO 線程上,由它負責SocketChannel 的讀寫和編解碼工作。Acceptor 線程池僅僅只用於客戶端的登陸、握手和安全認證,一旦鏈路建立成功,就將鏈路註冊到後端 subReactor 線程池的 IO 線程上,由 IO 線程負責後續的 IO 操作。
那些與Netty有關的知識點,你知道多少

無鎖設計、線程綁定

Netty 採用了串行無鎖化設計,在 IO 線程內部進行串行操作,避免多線程競爭導致的性能下降。表面上看,串行化設計似乎 CPU 利用率不高,併發程度不夠。但是,通過調整 NIO 線程池的線程參數,可以同時啓動多個串行化的線程並行運行,這種局部無鎖化的串行線程設計相比一個隊列-多個工作線程模型性能更優。

那些與Netty有關的知識點,你知道多少
Netty 的 NioEventLoop 讀取到消息之後,直接調用 ChannelPipeline 的fireChannelRead(Object msg),只要用戶不主動切換線程,一直會由 NioEventLoop 調用到用戶的 Handler,期間不進行線程切換,這種串行化處理方式避免了多線程操作導致的鎖的競爭,從性能角度看是最優的。

高性能的序列化框架

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

  1. SO_RCVBUF 和 SO_SNDBUF:通常建議值爲 128K 或者 256K。

小包封大包,防止網絡阻塞

  1. SO_TCPNODELAY:NAGLE 算法通過將緩衝區內的小封包自動相連,組成較大的封包,阻止大量小封包的發送阻塞網絡,從而提高網絡應用效率。但是對於時延敏感的應用場景需要關閉該優化算法。

軟中斷 Hash 值和 CPU 綁定

  1. 軟中斷:開啓 RPS 後可以實現軟中斷,提升網絡吞吐量。RPS 根據數據包的源地址,目的地址以及目的和源端口,計算出一個 hash 值,然後根據這個 hash 值來選擇軟中斷運行的 cpu,從上層來看,也就是說將每個連接和 cpu 綁定,並通過這個 hash 值,來均衡軟中斷在多個 cpu 上,提升網絡並行處理性能。

持續更新中…………

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章