netty源碼分析


1.netty底層實現原理

  1.netty:高性能、高並發網絡應用框架。

  2.如何做到高性能?高並發?

    1.對NIO進行封裝。

      1.NIO:基於事件驅動,(單線程模型,多線程模型,主從模型)

  3.selector:請求-->socket Channel-->selector(thread-->關注io讀寫事件)

    1.Channel(它負責基本的 IO 操作,例如:bind(),connect(),read(),write() 等等。同時它也是數據入站和出站的載體)

    2.EventLoop聚合了多路復用器Selector。消息的“出站”/“入站”就稱為事件(Event)。

    3.EventLoopGroup 要做的就是創建一個新的 Channel,並且生成一個EventLoop並分配給它。

    4.ChannelHandler:EventLoop 是事件的通知者,那么 ChannelHandler 就是事件的處理者

    5.ChannelPipeline:事件的處理順序是由 ChannelPipeline 來決定的。到 Channel 被創建的時候,ChannelHandler 會被 Netty 框架自動分配到 ChannelPipeline 上。

    6.ChannelHandlerContext:負責傳遞消息,每當有 ChannelHandler 添加到 ChannelPipeline 時,同時會創建 ChannelHandlerContext..

  4.Netty 的數據容器: 

  5.Bootstrap 對象:Bootstrap 的作用就是將 Netty 核心組件配置到程序中,並且讓他們運行起來。

   

  優秀原文鏈接:https://blog.csdn.net/suifeng629/article/details/103500016

  1.netty介紹:

    1.定義:Netty 是一個異步的、基於事件驅動的網絡應用框架,它可以用來開發高性能服務端和客戶端。(實際並非異步)

    2.為了滿足高並發下網絡請求,引入了 NIO 的概念。Netty 是針對 NIO 的實現,在 NIO 封裝,網絡調用,數據處理以及性能優化等方面都有不俗的表現。

    3.Selector 機制就是 NIO 的核心。

      1.當每次客戶端請求時,會創建一個 Socket Channel,並將其注冊到 Selector 上(多路復用器);

      2.Selector 關注服務端 IO 讀寫事件,此時客戶端並不用等待 IO 事件完成,可以繼續做接下來的工作;

      3.一旦,服務端完成了 IO 讀寫操作,Selector 會接到通知,同時告訴客戶端 IO 操作已經完成;

      4.接到通知的客戶端,就可以通過 SocketChannel 獲取需要的數據了。

      

  2.netty的優點:

    1.對 NIO 進行封裝,開發者不需要關注 NIO 的底層原理,只需要調用 Netty 組件就能夠完成工作。

    2.對網絡調用透明,從 Socket 建立 TCP 連接到網絡異常的處理都做了包裝。

    3.對數據處理靈活, Netty 支持多種序列化框架,通過“ChannelHandler”機制,可以自定義“編/解碼器”。

    4.對性能調優友好,Netty 提供了線程池模式以及 Buffer 的重用機制(對象池化),不需要構建復雜的多線程模型和操作隊列。

  3.netty場景:

    1.Netty 作為 NIO 的實現,它適用於服務器/客戶端通訊的場景;

    2.以及針對於 TCP 協議下的高並發應用。

2.netty核心組件:

  1.Channel

    1.當客戶端和服務端連接的時候會建立一個 Channel。

    2.這個 Channel 我們可以理解為 Socket 連接,它負責基本的 IO 操作,例如:bind(),connect(),read(),write() 等等。

    3.簡單的說,Channel 就是代表連接,同時它也是數據入站和出站的載體。

  2.EventLoop 和 EventLoopGroup

    1.EventLoop:消息的“出站”/“入站”就會產生事件(Event),那么就有一個機制去監控和協調事件。這個機制就是EventLoop。Netty的IO線程EventLoop聚合了多路復用器Selector。

    2.在 Netty 中每個 Channel 都會被分配到一個 EventLoop。

      1.一個 EventLoop 可以服務於多個 Channel。

      2.每個 EventLoop 會占用一個 Thread,同時這個 Thread 會處理 EventLoop 上面發生的所有 IO 操作和事件。

    3.圖形展示:(channel1、channel2、channel3)-eventloop-thread-(io,io,io)

    

 

    4.EventLoopGroup :

      1.EventLoopGroup 要做的就是創建一個新的 Channel,並且生成一個EventLoop並分配給它。

      2.一個 EventLoopGroup 中包含了多個 EventLoop 對象。

      

 

       3.模擬請求流程

        1.客戶端發送消息到服務端,EventLoop 發現以后會告訴服務端:“你去獲取消息”,同時客戶端進行其他的工作。

        2.當 EventLoop 檢測到服務端返回的消息,也會通知客戶端:“消息返回了,你去取吧“,客戶端再去獲取消息。

        3.整個過程 EventLoop 就是監視器+傳聲筒。

  3.ChannelHandler,ChannelPipeline 和 ChannelHandlerContext

    1.ChannelHandler:EventLoop 是事件的通知者,那么 ChannelHandler 就是事件的處理者

      1.ChannelHandler 中可以添加一些業務代碼,例如數據轉換,邏輯運算等等。

      2.Server 和 Client 分別都有一個 ChannelHandler 來處理,讀取信息,網絡可用,網絡異常之類的信息。

      3.針對出站和入站的事件,有不同的 ChannelHandler。

        1.ChannelInBoundHandler(入站事件處理器)

        2.ChannelOutBoundHandler(出站事件處理器)

      

    2.ChannelPipeline :事件的處理順序是由 ChannelPipeline 來決定的。

    

 

       1.ChannelPipeline 為 ChannelHandler 鏈提供了容器。到 Channel 被創建的時候,會被 Netty 框架自動分配到 ChannelPipeline 上。

       2.ChannelPipeline 保證 ChannelHandler 按照一定順序處理事件,當事件觸發以后,會將數據通過 ChannelPipeline 按照一定的順序通過 ChannelHandler。

       3.ChannelPipeline 也可以添加或者刪除 ChannelHandler,管理整個隊列。

    3.ChannelHandlerContext :每當有 ChannelHandler 添加到 ChannelPipeline 時,同時會創建 ChannelHandlerContext.

      1.ChannelHandlerContext 的主要功能是管理 ChannelHandler 和 ChannelPipeline 的交互,ChannelHandlerContext 負責傳遞消息.

      2.ChannelHandlerContext 參數貫穿 ChannelPipeline,將信息傳遞給每個 ChannelHandler。如下圖:

      

 

  4.netty核心組件關系圖:

    

 

  5.Netty 的數據容器:

    1.ByteBuf 工作原理:

      1.從結構上來說,ByteBuf 由一串字節數組構成。數組中每個字節用來存放信息。

      2.ByteBuf 提供了兩個索引,一個用於讀取數據,一個用於寫入數據。這兩個索引通過在字節數組中移動,來定位需要讀或者寫信息的位置。

      3.如果 readerIndex 超過了 writerIndex 的時候,Netty 會拋出 IndexOutOf-BoundsException 異常。

    2.ByteBuf 使用模式:

      1.堆緩沖區:

        1.ByteBuf 將數據存儲在 JVM 的堆中,通過數組實現,可以做到快速分配。

        2.由於在堆上被 JVM 管理,在不被使用時可以快速釋放。可以通過 ByteBuf.array() 來獲取 byte[] 數據。

      2.直接緩沖區:

        1.在 JVM 的堆之外直接分配內存,用來存儲數據。其不占用堆空間,使用時需要考慮內存容量。

        2.它在使用 Socket 傳遞時性能較好,因為間接從緩沖區發送數據,在發送之前 JVM 會先將數據復制到直接緩沖區再進行發送。

        3.直接緩沖區的數據分配在堆之外,通過 JVM 進行垃圾回收,並且分配時也需要做復制的操作,因此使用成本較高。

      3.復合緩沖區:

        1.顧名思義就是將上述兩類緩沖區聚合在一起。Netty 提供了一個 CompsiteByteBuf,可以將堆緩沖區和直接緩沖區的數據放在一起,讓使用更加方便。

    3.ByteBuf 的分配

      Netty 提供了兩種 ByteBufAllocator 的實現

      1.PooledByteBufAllocator,實現了 ByteBuf 的對象的池化,提高性能減少內存碎片。

        1.對象池化的技術和線程池,比較相似,目的是提高內存的使用率。

        2.池化技術簡單實現思路:是在 JVM 堆內存上構建一層內存池,通過 allocate 方法獲取內存池中的空間,通過 release 方法將空間歸還給內存池。

        3.池化技術算法優化:伙伴系統

          1.用完全二叉樹管理內存區域,左右節點互為伙伴,每個節點代表一個內存塊。內存分配將大塊內存不斷二分,直到找到滿足所需的最小內存分片。

          2.內存釋放會判斷釋放內存分片的伙伴(左右節點)是否空閑,如果空閑則將左右節點合成更大塊內存。

      2.Unpooled-ByteBufAllocator,沒有實現對象的池化,每次會生成新的對象實例。

        1.對象的生成和銷毀,會大量地調用 allocate 和 release 方法,因此內存池面臨碎片空間回收的問題,在頻繁申請和釋放空間后,內存池需要保證連續的內存空間,用於對象的分配。

        2.對象生成和銷毀算法優化:slab 系統,主要解決內存碎片問題,將大塊內存按照一定內存大小進行等分,形成相等大小的內存片構成的內存集。

          1.按照內存申請空間的大小,申請盡量小塊內存或者其整數倍的內存,釋放內存時,也是將內存分片歸還給內存集。

  6.Bootstrap 對象

    1.描述:Bootstrap 的作用就是將 Netty 核心組件配置到程序中,並且讓他們運行起來。

    2.結構:從 Bootstrap 的繼承結構來看,Bootstrap 和 ServerBootstrap,一個對應客戶端的引導,另一個對應服務端的引導

    

 

    3.客戶端引導 Bootstrap:

      主要有兩個方法 bind() 和 connect()。

      1.Bootstrap 通過 bind() 方法創建一個 Channel。

      2.在 bind() 之后,通過調用 connect() 方法來創建 Channel 連接。

      3.Bootstrap(客戶端引導)只要知道服務端 IP 和 Port 建立連接就可以了.

      4.Bootstrap(客戶端引導)需要一個 EventLoopGroup.

    4.服務端引導 ServerBootstrap:

      1.與客戶端不同的是在 Bind() 方法之后會創建一個 ServerChannel;

      2.這個ServerChannel不僅會創建新的 Channel 還會管理已經存在的 Channel。

      3.ServerBootstrap(服務端引導)綁定一個端口,用來監聽客戶端的連接請求.

      4.ServerBootstrap(服務端引導)則需要兩個 EventLoopGroup.

        因為服務器需要兩組不同的 Channel.

        1.第一組 ServerChannel 自身監聽本地端口的套接字。

        2.第二組用來監聽客戶端請求的套接字。

      

3.netty調用流程,代碼實現

  假設有一個客戶端去調用一個服務端,假設服務端叫做 EchoServer,客戶端叫做 EchoClient,用 Netty 架構實現代碼如下。

  1.服務端代碼:

    1.構建服務器端,假設服務器接受客戶端傳來的信息。在構造函數中傳入需要監聽的端口號。

      

 

    2.接下來就是服務的啟動方法:

      1.啟動 NettyServer 的 Start 方法

      

      2.方法做了如下事:

       1.創建 EventLoopGroup。

       2.創建 ServerBootstrap。

       3.指定所使用的 NIO 傳輸 Channel。

       4.使用指定的端口設置套接字地址。

       5.添加一個 ServerHandler 到 Channel 的 ChannelPipeline。

       6.異步地綁定服務器;調用 sync() 方法阻塞等待直到綁定完成。

       7.獲取 Channel 的 CloseFuture,並且阻塞當前線程直到它完成。

       8.關閉 EventLoopGroup,釋放所有的資源。

    3.NettyServer 啟動以后會監聽某個端口的請求,當接受到了請求就需要處理了。

       1.可以通過 ChannelInboundHandlerAdapter 實現

      

 

 

       2.上圖這三個方法都是根據事件觸發的

        1.當接收到消息時的操作,channelRead。

        2.消息讀取完成時的方法,channelReadComplete。

        3.出現異常時的方法,exceptionCaught。

  2.客戶端代碼:

    1.客戶端和服務端的代碼基本相似,在初始化時需要輸入服務端的 IP 和 Port。如下圖

    

     2.同樣在客戶端啟動函數中包括以下內容:如下圖

    

       1.上圖客戶端啟動程序的順序如下:

        1.創建 Bootstrap。

        2.指定 EventLoopGroup 用來監聽事件。

        3.定義 Channel 的傳輸模式為 NIO(Non-BlockingInputOutput)。

        4.設置服務器的 InetSocketAddress。

        5.在創建 Channel 時,向 ChannelPipeline 中添加一個 EchoClientHandler 實例。

        6.連接到遠程節點,阻塞等待直到連接完成。

        7.阻塞,直到 Channel 關閉。

        8.關閉線程池並且釋放所有的資源。

    3.客戶端在完成以上操作以后,會與服務端建立連接從而傳輸數據。同樣在接受到 Channel 中觸發的事件時,客戶端會觸發對應事件的操作。

    

4.io/nio/bio/reactor(多路復用模式)/netty的區別:

原文網址:https://www.cnblogs.com/crazymakercircle/p/10225159.html

  java IO讀寫的底層流程:

  

 

  阻塞io:

    1.含義:指的是需要內核IO操作徹底完成后,才返回到用戶空間,執行用戶的操作.

    2.描述:阻塞指的是用戶空間程序的執行狀態,用戶空間程序需等到IO操作徹底完成。

  非阻塞io:

    1.含義:指的是用戶程序不需要等待內核IO操作完成后,內核立即返回給用戶一個狀態值,用戶空間無需等到內核的IO操作徹底完成,可以立即返回用戶空間。

    2.描述:執行用戶的操作,處於非阻塞的狀態。

 

   1.同步阻塞io(blocking io)-bio:

    1.優點:程序簡單,在阻塞等待數據期間,用戶線程掛起。用戶線程基本不會占用 CPU 資源。

    2.缺點:BIO模型在高並發場景下是不可用的。

  2.同步非阻塞io(Non-blocking IO)-nio:

    1.優點:每次發起的 IO 系統調用,在內核的等待數據過程中可以立即返回。用戶線程不會阻塞,實時性較好。

    2.缺點:需要不斷的重復發起IO系統調用,這種不斷的輪詢,將會不斷地詢問內核,這將占用大量的 CPU 時間,系統資源利用率較低。NIO模型在高並發場景下,也是不可用的。

  3.io多路復用(io Multiplexing):經典的Reactor設計模式。

    1.異步阻塞IO,Java中的Selector和Linux中的epoll都是這種模型。

    2.目前支持IO多路復用的系統調用有 select,epoll等等。

    3.基本原理:IO多路復用模型的基本原理就是select/epoll系統調用,單個線程不斷的輪詢select/epoll系統調用所負責的成百上千的socket連接,當某個或者某些socket網絡連接有數據到達了,就返回這些可以讀寫的連接。

    4.優點:用select/epoll的優勢在於,它可以同時處理成千上萬個連接(connection),系統不必創建線程,也不必維護這些線程,從而大大減小了系統的開銷。

 

  4.異步Io:

    1.描述:有點類似於Java中比較典型的模式是回調模式。

  5.select/poll/epoll的區別: 

    原文鏈接:https://blog.csdn.net/h2604396739/article/details/82534253

    三種方式區別

    1..select的幾大缺點:

      1.每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大;

      2.同時每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大;

      3.select支持的文件描述符數量太小了,默認是1024.  

    2.poll實現
      poll的實現和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結構而不是select的fd_set結構.

    3.epoll(reference Link)
      epoll既然是對select和poll的改進,就應該能避免上述的三個缺點。那epoll都是怎么解決的呢?在此之前,我們先看一下epoll和select和poll的調用接口上的不同,select和poll都只提供了一個函數——select或者poll函數。而epoll提供了三個函數,                epoll_create,epoll_ctl和epoll_wait,epoll_create是創建一個epoll句柄;epoll_ctl是注冊要監聽的事件類型;epoll_wait則是等待事件的產生。

  6.Java的NIO(new io)技術,使用的就是IO多路復用模型,在linux系統上,使用的是epoll系統調用。

    1.new io原理:https://www.cnblogs.com/funyoung/p/11810581.html

      1.NIO主要有三個核心部分:Selector、Channel、Buffer

      2.數據總是從Channel讀取到Buffer或者從Buffer寫入到Channel中。

      3.Selector可以監聽多個Channel的多個事件。

    2.傳統的IO與Channel的區別:

       1.傳統的IO是BIO的,而Channel是NIO的。

          *當流調用了read()、write()方法后會一直阻塞線程直到數據被讀取或寫入完畢。

       2.傳統IO流是單向的,而Channel是雙向的。

  7.Reactor模式原理:

    1.Reactor模式是在NIO下實現的

    2.Reactor有三種模式:

      1.Reactor單線程模式

      2.Reactor多線程模式

      3.主從Reactor多線程模式

      

  

    

    

 

 

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM