Netty零拷貝實現原理

1. 傳統數據傳送

傳統數據從Socket網絡中傳送,需要4次數據拷貝和4次上下文切換:

  1. 將磁盤文件,讀取到操作系統內核緩衝區;
  2. 將內核緩衝區的數據,拷貝到用戶空間的緩衝區;
  3. 數據從用戶空間緩衝區拷貝到內核的socket網絡發送緩衝區;
  4. 數據從內核的socket網絡發送緩衝區拷貝到網卡接口(硬件)的緩衝區,由網卡進行網絡傳輸。

傳統方式,讀取磁盤文件並進行網絡發送,經過的4次數據拷貝和4次上下文切換是非常繁瑣的。實際IO讀寫,需要進行IO中斷,需要CPU響應中斷(帶來上下文切換),儘管後來引入DMA來接管CPU的中斷請求,但四次拷貝仍在存在不必要的環節。

2. 零拷貝實現原理

零拷貝的目的是爲了減少IO流程中不必要的拷貝,以及減少用戶進程地址空間和內核地址空間之間因爲上下文切換而帶來的開銷。由於虛擬機不能直接操作內核,因此它的實現需要操作系統OS的支持,也就是需要kernel內核暴漏API。

2.1 Netty中的零拷貝

  1. Direct Buffers:Netty的接收和發送ByteBuffer採用直接緩衝區(Direct Buffer)實現零拷貝,直接在內存區域分配空間,避免了讀寫數據的二次內存拷貝,這就實現了讀寫Socket的零拷貝。

如果使用傳統的堆內存緩衝區(Heap Buffer)進行Socket讀寫,JVM會將堆內存Buffer拷貝到直接內存中,然後才寫入Socket中。相比堆外直接內存,消息在發送過程中多了一次緩衝區的內存拷貝。

  1. CompositeByteBuf:它可以將多個ByteBuf封裝成ByteBuf,對外提供統一封裝後的ByteBuf接口。CompositeByteBuf並沒有真正將多個Buffer組合起來,而是保存了它們的引用,從而避免了數據的拷貝,實現了零拷貝。

傳統的ByteBuffer,如果需要將兩個ByteBuffer中的數據組合到一起,我們需要首先創建一個size=size1+size2大小的新的數組,然後將兩個數組中的數據拷貝到新的數組中。但是使用Netty提供的組合ByteBuf,就可以避免這樣的操作。

  1. Netty的文件傳輸類DefaultFileRegion通過調用FileChannel.transferTo()方法實現零拷貝,文件緩衝區的數據會直接發送給目標Channel。底層調用Linux操作系統中的sendfile()實現的,數據從文件由DMA引擎拷貝到內核read緩衝區,;DMA從內核read緩衝區將數據拷貝到網卡接口(硬件)的緩衝區,由網卡進行網絡傳輸。

2.2 Java中零拷貝

  1. 通過Java的FileChannel.transferTo()方法實現零拷貝,底層是調用Linux操作系統中的sendfile()實現的,數據從文件由DMA引擎拷貝到內核read緩衝區;DMA從內核read緩衝區將數據拷貝到網卡接口(硬件)的緩衝區,由網卡進行網絡傳輸。

  2. 通過Java的FileChannel.map()方法實現零拷貝,底層是調用Linux操作系統中的mmap()實現的,將內核緩衝區的內存和用戶緩衝區的內存做了一個地址映射,這種方式適合讀取大文件,同時也能對文件內容進行更改,但是如果其後要通過SocketChannel發送,還是需要CPU進行數據的拷貝。

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