Go语言基础之零拷贝及应用 | 青训营

78 阅读2分钟

深入浅出零拷贝及应用

一、传统文件传输过程

1、Linux文件的读写流程

分享数据的过程

  1. 用户视角:小明通过点击分享,途经网络传输,将消息传送给小明的朋友。于是,对方即可收到消息
  2. 操作系统视角
//从磁盘读取文件
read(file,tmp_buf,len);
//通过网络协议发送给客户端
write(socket,tmp_buf,len);

2、Linux传统文件传输过程

image.png

//发送文件名给接收端
_, err = conn.Write([]byte(fileName))
if err != nil {
        fmt.Println("conn.Write err:", err)
        return
}
//读取服务器回发的信息
buf := make([]byte, 16)
n, err := conn.Read(buf)
if err != nil {
        fmt.Println("conn.Read err:", err)
        return
}

3、传统I/O操作中的瓶颈

内核空间为什么不能直接使用用户空间的数据呢?

  • 内核不能信任任何用户空间的指针
  • 稳定性与效率

4、CPU数据拷贝方式

(1)、I/O轮询

  • 优点:实现简单
  • 缺点:占用CPU全部资源,效率低 image.png (2)、I/O中断
  • 优点:对比IO轮询的方式,一定程度上释放了CPU资源
  • 缺点:在大数据量传输的情况下,CPU会反复中断 image.png (3)、DMA传输
  • 优点:彻底减少了一次CPU拷贝
  • 缺点:依赖设备硬件支持 image.png

二、零拷贝的实现方式

1、零拷贝定义:

1)、减少用户空间和内核空间之间的CPU拷贝次数 2)、减少上下文切换次数

2、介绍零拷贝的多种实现方式:

  • mmap + write image.png
  • sendfile image.png
  • sendfile + gather

image.png

  • splice image.png
  • 总结

image.png

三、Go语言中的实现

mmap + write的使用

map_file, err := os.Create("./input.txt")
if err != nil {
    fmt.Println(err)
    os.Exit(1)
}
mmap, err := syscall.Mmap(int(map_file.Fd()),0,10,syscall.PROT_READ|syscall.PROT_WRITE, Syscall.MAP_SHARED)
if err != nil {
    fmt.Println(err)
    os.Exit(1)
}
ioutil.WriteFile("./result.txt",mmap,0666)

四、零拷贝的应用

Kafka vs RocketMQ 如何通过零拷贝技术提升吞吐率与实现百万级并发

一、Kafka发送消息的过程 image.png

Kafka性能优化点

1、顺序读写:

  • producer每次会追加写入到partition
  • consumer每次消费的时候,根据offset进行顺序读取
  • 批量刷盘

2、页缓存技术:

  • 利用linux的page cache技术
  • 异步落盘,减少磁盘I/O次数
  • 通过Replication机制去解决数据丢失的问题

3、零拷贝mmap: image.png

4、零拷贝sendfile:

  • consumer不需要对数据进行修改,可以采用零拷贝方式
  • 对比传统read() + write():节省2次cpu拷贝、2次上下文切换

image.png

二、RocketMQ发送消息的过程

image.png

1、RocketMQ优化点:mmap

  • 通过mmap + write()实现,满足小数据需求
  • 通过文件预热的方式来解决缺页的问题

2、RocketMQ vs Kafka

image.png

3、各种拷贝实现方式对比 image.png