Kafka之所以那么快的另外一個原因就是零拷貝(zero-copy)技術。本文我們就來了解Kafka中使用的零拷貝技術為什么那么快。
傳統的文件拷貝
傳統的文件拷貝通常需要從用戶態去轉到核心態,經過read buffer,然后再返回到用戶態的應用層buffer,然后再從用戶態把數據拷貝到核心態的socket buffer,然后發送到網卡。如下圖所示:

從上圖你會發現,傳統的數據傳輸需要多次的用戶態和核心態之間的切換,而且還要把數據復制多次,最終才打到網卡。
如果減少了用戶態與核心態之間的切換,是不是就會更快了呢?如下圖:

此時我們會發現用戶態“空空如也”。數據沒有來到用戶態,而是直接在核心態就進行了傳輸,但這樣依然還是有多次復制。首先數據被讀取到read buffer中,然后發到socket buffer,最后才發到網卡。雖然減少了用戶態和核心態的切換,但依然存在多次數據復制。
如果可以進一步減少數據復制的次數,甚至沒有數據復制是不是就會做到最快呢?
DMA
別急,這里我們先介紹一個新的武器:DMA。
DMA,全稱叫Direct Memory Access,一種可讓某些硬件子系統去直接訪問系統主內存,而不用依賴CPU的計算機系統的功能。聽着是不是很厲害,跳過CPU,直接訪問主內存。傳統的內存訪問都需要通過CPU的調度來完成。如下圖:

而DMA,則可以繞過CPU,硬件自己去直接訪問系統主內存。如下圖:

很多硬件都支持DMA,這其中就包括網卡。

零拷貝
回到本文中的文件傳輸,有了DMA后,就可以實現絕對的零拷貝了,因為網卡是直接去訪問系統主內存的。如下圖:

Java的零拷貝實現
在Java中的零拷貝實現是在FileChannel中,其中有個方法transferTo(position,fsize,src)。
傳統的文件傳輸是通過java.io.DataOutputStream,java.io.FileInputStream來實現的,然后通過while循環來讀取input,然后寫入到output中。

零拷貝則是通過java.nio.channels.FileChannel中的transferTo方法來實現的。transferTo方法底層是基於操作系統的sendfile這個system call來實現的(不再需要拷貝到用戶態了),sendfile負責把數據從某個fd(file descriptor)傳輸到另一個fd。
sendfile:


Java的transferTo:

傳統方式與零拷貝性能對比

可以看出速度快出至少三倍多。Kafka在文件傳輸的過程中正是使用了零拷貝技術對文件進行拷貝。建議以后多用FileChannel的transferTo吧。
回顧
- 傳統的文件傳輸有多次用戶態和內核態之間的切換,而且文件在多個buffer之間要復制多次最終才被發送到網卡。
- DMA是一種硬件直接訪問系統主內存的技術。
- 多種硬件都已使用了DMA技術,其中就包括網卡(NIC)。
- DMA技術讓CPU得到解放,讓CPU可以不用一直守着來完成文件傳輸。
- 零拷貝技術減少了用戶態與內核態之間的切換,讓拷貝次數降到最低,從而實現高性能。
- Kafka使用零拷貝技術來進行文件的傳輸。