深入淺出Netty:write

佔小狼發表於2016-09-19

本系列:


上一章節中,分析了Netty如何處理read事件,本節分析Netty如何把資料寫會客戶端。

把資料返回客戶端,需要經歷三個步驟:

  • 1、申請一塊快取buf,寫入資料。
  • 2、將buf儲存到ChannelOutboundBuffer中。
  • 3、將ChannelOutboundBuffer中的buff輸出到socketChannel中。

為什麼需要把buf儲存到ChannelOutboundBuffer?

ctx.write()實現:

預設情況下,findContextOutbound()會找到pipeline的head節點,觸發write方法。

outboundBuffer 隨著Unsafe一起例項化,最終將msg通過outboundBuffer封裝起來。

ChannelOutboundBuffer內部維護了一個Entry連結串列,並使用Entry封裝msg。

1、unflushedEntry:指向連結串列頭部
2、tailEntry:指向連結串列尾部
3、totalPendingSize:儲存msg的位元組數
4、unwritable:不可寫標識

通過Entry.newInstance返回Entry例項,Netty對Entry採用了快取策略,使用完的Entry例項需要清空並回收,難道是因為Entry例項化比較耗時?

新的entry預設插入連結串列尾部,並讓tailEntry指向它。

2184951-53e95abefcc0504f

方法incrementPendingOutboundBytes主要採用CAS更新totalPendingSize欄位,並判斷當前totalPendingSize是否超過閾值writeBufferHighWaterMark,預設是65536。如果totalPendingSize >= 65536,則採用CAS更新unwritable為1,並觸發ChannelWritabilityChanged事件。

到此為止,全部的buf資料已經儲存在outboundBuffer中。

ctx.flush()實現:

預設情況下,findContextOutbound()會找到pipeline的head節點,觸發flush方法。

方法addFlush主要對write過程新增的msg進行flush標識,其實我不清楚,這個標識過程有什麼意義。

直接看flush0方法:

1、如果當前selectionKey 是寫事件,說明有執行緒執行flush過程,則直接返回。
2、否則直接執行flush操作。

1、如果當前socketChannel已經關閉,或斷開連線,則執行失敗操作。
2、否則執行doWrite把資料寫入到socketChannel。

1、size方法返回outboundBuffer有多少Entry例項。
2、in.nioBuffers()負責把Entry中儲存的ByteBuf型別的msg,重新返回Nio的ByteBuffer例項,並返回ByteBuffer陣列nioBuffers,其實msg和ByteBuffer例項指向的是同一塊記憶體,因為在UnpooledDirectByteBuf實現類中,已經維護了ByteBuffer的例項。
3、socketChannel.write()方法把nioBuffers的資料寫到socket中,這是Nio中的實現。

到此為止,nioBuffers的資料都flush到socket,客戶端可以準備接收了。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

深入淺出Netty:write

相關文章