當前位置:編程學習大全網 - 源碼下載 - Netty writeAndFlush分析

Netty writeAndFlush分析

Netty事件分為入站事件和出站事件,可以通過ChannelPipline或ChannelHandlerContext進行傳播。入站事件通過ChannelPipeline傳播,它將從ChannelPipeline的頭傳播到channel pipeline的末端,出站事件將從末端傳輸到頭。入站事件通過ChannelHandlerContext傳播,將從下壹個ChannelHandler傳遞到末尾,出站事件將從下壹個ChannelHandler傳遞到頭。

為了提高數據傳輸的效率,Netty在寫數據時會先將數據(ByteBuf)緩存到ChannelOutboundBuffer中,直到調用flush方法才會將ChannelOutboundBuffer中的數據寫入socket緩沖區。

ChannelOutboundBuffer中有三個重要的屬性:

從它的屬性可以看出,ChannelOutboundBuffer是壹個鏈表結構,有三個指針:

ChannelOutboundBuffer中有兩個重要的方法:addMessage:以鏈表的形式緩存數據,addFlush:移動鏈表的指針,將緩存的數據標記為刷新。請註意,此時數據不會寫入套接字緩沖區。接下來,讓我們看看兩個方法的實現:

讓我們進入它的addMessage方法,並分析它如何緩存數據:

當第壹次添加數據的數量時,數據將被封裝為壹個條目。此時,tailEntry和unflushedEntry指針指向該條目,flushedEntry指針為空。每次添加數據時,都會生成壹個新的條目,tailEntry指針將指向該條目,而unflushedEntry指針將始終指向最初添加的條目。我們用圖來表示壹下:

第壹次添加:

第n次加法:

為了防止緩存數據過大,Netty限制了緩存數據的大小:

addMessage方法將最終調用incrementPendingOutboundBytes方法來記錄緩存的數據大小(totalPendingSize)。如果大小超過寫緩沖區的高水位閾值(默認為64K),將更新未寫入標誌,並且將傳播通道的不可寫入狀態已更改的事件:

移動鏈表的指針,將緩存的數據標記為刷新,並將每個數據節點的狀態設置為可取消:

執行addFlush方法後,鏈表如下所示:

通過ChannelHandlerContext # writeandFlush方法,分析Netty如何通過網絡傳輸數據:

channelhandlercontext # writeAndFlush方法最終將調用其子類abstracthandlercontext的writeAndFlush方法:

主要邏輯在寫入方法中:

write方法主要做兩件事。壹個是找到下壹個ChannelHandlerContext。第二種是調用下壹個ChannelHandlerContext的w riteAndFlush方法來傳播事件。writeAndFlush方法是壹個出站事件。如前所述,對於出站事件,事件通過ChannelHandlerContext傳播。該事件是從pipline鏈中查找當前香奈兒Handlercontext的下壹個香奈兒Handlercontext,並將其傳播到HeadContext。在此期間,我們需要壹個自定義的編碼器對傳輸過來的Java對象進行編碼,轉換成ByteBuf對象,最後事件會傳遞給HeadContext進行處理。

invokeWriteAndFlush方法主要做兩件事。壹種是調用invokeWrite0方法將數據放入Netty緩沖區,另壹種是通過NioSocketChannel調用invokeFlush0方法將緩沖區數據寫入socket緩沖區。

在內部,invokeWrite0方法將調用ChannelOutboundHandler#write方法:

如前所述,出站事件最終會傳播到HeadContext。在傳播到HeadContext之前,我們需要自定義編碼器對Java對象進行編碼,並將Java對象編碼為ByteBuf。關於編碼器的這壹章暫且不分析。我們輸入HeadContext的寫方法:

在HeadContext#write方法中調用AbstractChannelUnsafe#write方法:

這個方法主要做三件事:第壹,對數據進行過濾和轉換;第二,估算數據大小;第三,緩存數據。讓我們先來看看filterOutboundMessage方法:

1.過濾和轉換數據:

filterOutboundMessage方法會先過濾數據,如果數據不是ByteBuf或FileRegion類型,會直接拋出異常。如果數據為ByteBuf類型,則判斷數據是否為直接內存,如果不是,則轉換為直接內存以提高性能。

二、估計數據量:

第三,緩存數據:

最後,將調用通道出站緩沖區# addMessage方法將數據緩存到鏈表中。對於addmessage方法,您可以查看本文中的Netty buffer壹節。

回到abstractchannelhandlercontext # invokewriteandflush方法。在調用invokeWrite0方法將數據放入緩存後,將調用invokeFlush0方法將緩存中的數據寫入套接字緩沖區。ChannelOutboundHandler#flush方法將在invokeFlush0方法中調用:

flush方法最終會將事件傳播到HeadContext的flush方法:

在HeadContext#flush方法中調用AbstractChannelUnsafe#flush方法:

該方法主要做兩件事。壹種是調用ChannelOutboundBuffer#addFlush方法,移動鏈表指針,將緩存的數據標記為刷新。其次,調用flush0方法將緩存中的數據寫入套接字緩沖區。對於addFlush方法,請參考文章的Netty Buffer部分,我們將直接進入flush0方法:

flush0方法主要做兩件事:壹是確定是否有掛起的刷新。二:調用父類flush0方法。

首先,判斷是否有掛起的刷新。

文中提到,寫數據時,當socket緩沖區沒有空閑空間時,會設置未寫狀態,並註冊OP_WRITE事件。當套接字緩沖區中有空閑空間時,將觸發forceFlush。讓我們輸入isFlushPending方法,看看該方法是如何判斷的:

其次,調用父類flush0方法。

寫入套接字緩沖區的特定邏輯在AbstractChannel#AbstractUnsafe父類中:

核心邏輯在doWrite方法中,我們進入AbstractChannel子類NioSocketChannel的doWrite方法來看具體實現:

NioSocketChannel#doWrite方法根據nioBufferCnt的大小執行不同的寫邏輯,如果為0,則調用AbstractNioByteChannel#doWrite方法。如果nioBufferCnt為1或大於1,則調用NioSocketChannel的不同重載方法進行處理。註意,寫數據時,自旋數默認為16,也就是說,如果寫了16次仍有未完成的數據,則調用未完成寫方法,將刷新操作封裝為壹個任務放入隊列,以免阻塞其他任務。另外,如果調用NioSocketChannel#write方法後,返回的localWrittenBytes為0,說明socket緩沖區空間不足,那麽註冊OP_WRITE事件,有可用空間時會觸發該事件,然後調用forceFlush方法繼續寫數據。

  • 上一篇:如何重新簽名ipa文件
  • 下一篇:聖安地列斯怎麽制作mod
  • copyright 2024編程學習大全網