系列前言
kafka作爲一個處理實時數據和日誌的管道,每秒可以處理幾十萬條消息。其瓶頸自然也在I/O層面,所以其高吞吐背後離不開如下幾個特性:
- NIO
- 磁盤順序讀寫
- Queue數據結構的極致使用
- 分區提高併發
- 零拷貝提高效率
- 異步刷盤
- 壓縮提高數據傳輸效率
本次我將從kafka的源碼分析其ZeroCopy模塊的細節。
ZeroCopy基礎概念
傳統IO
- JVM虛擬機發送一個read()操作系統級別的方法
- 產生一個上下文的切換,從程序所在的用戶空間切換至系統的內核空間
- 內核空間向磁盤空間請求數據,通過DMA直接內存訪問的方式將數據讀取到內核空間緩衝區
- 用戶空間是無法直接使用,需要將這份緩衝數據原封不動的拷貝到用戶空間
- 在用戶空間裏read數據
有兩次上下文的切換,和兩次數據的拷貝。
ZeroCopy是什麼
零拷貝是指計算機操作的過程中,CPU不需要爲數據在內存之間的拷貝消耗資源。而它通常是指計算機在網絡上發送文件時,不需要將文件內容拷貝到用戶空間(User Space)而直接在內核空間(Kernel Space)中傳輸到網絡的方式。
零拷貝技術減少了用戶態與內核態之間的切換,讓拷貝次數降到最低,從而實現高性能。
Java中的ZeroCopy
在Java中的零拷貝實現是在NIO的FileChannel中,其中有個方法
- transferTo
- transferFrom
kafka實現
具體使用
在org.apache.kafka.common.record.FileRecords中具體使用到了ZeroCopy。
kafak版本:2.2.0 https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/record/FileRecords.java
// org.apache.kafka.common.record.FileRecords
@Override
public long writeTo(GatheringByteChannel destChannel, long offset, int length) throws IOException {
long newSize = Math.min(channel.size(), end) - start;
int oldSize = sizeInBytes();
if (newSize < oldSize)
throw new KafkaException(String.format(
"Size of FileRecords %s has been truncated during write: old size %d, new size %d",
file.getAbsolutePath(), oldSize, newSize));
long position = start + offset;
int count = Math.min(length, oldSize);
final long bytesTransferred;
if (destChannel instanceof TransportLayer) {
TransportLayer tl = (TransportLayer) destChannel;
bytesTransferred = tl.transferFrom(channel, position, count);
} else {
bytesTransferred = channel.transferTo(position, count, destChannel);
}
return bytesTransferred;
}
使用場景
- partition leader到follower的消息同步
- consumer拉取partition中的消息
後面講具體分析這塊的邏輯。
參考
- Kafka Zero-Copy 使用分析 https://www.jianshu.com/p/d47de3d6d8ac