broker 维护的消息日志本身就是一个文件目录,每个文件都由一系列以相同格式写入到磁盘的消息集合组成,这种写入格式被 producer 和 consumer 共用。保持这种通用格式可以对一些很重要的操作进行优化: 持久化日志块的网络传输。 现代的unix 操作系统提供了一个高度优化的编码方式,用于将数据从 pagecache (页缓存)转移到 socket 网络连接中;在 Linux 中系统调用 sendfile
做到这一点。
为了理解 sendfile
的意义,了解原本数据从文件到套接字的常见数据传输路径就非常重要:如下图
原始方法:
1、操作系统从磁盘读取数据到内核空间的 pagecache
2、应用程序读取内核空间的数据到用户空间的缓冲区
3、应用程序将数据(用户空间的缓冲区)写回内核空间到套接字缓冲区(内核空间)
4、操作系统将数据从套接字缓冲区(内核空间)复制到通过网络发送的 NIC 缓冲区(网卡缓冲区)
实际调用函数内容是
1:调用read函数,文件数据copy到内核缓冲区
2:read函数返回,文件数据从内核缓冲区copy到用户缓冲区
3:write函数调用,将文件数据从用户缓冲区copy到内核与socket相关的缓冲区
4:数据从socket缓冲区copy到相关协议引擎。
这显然是低效的,有四次 copy 操作和两次系统调用。使用 sendfile 方法,可以允许操作系统将数据从 pagecache 直接发送到网络,这样避免重新复制数据。所以这种优化方式,只需要最后一步的copy操作,将数据复制到 NIC 缓冲区(网卡缓冲区)。如下图
零拷贝方式:
“零拷贝技术”只用将磁盘文件的数据复制到页面缓存中一次,然后将数据从页面缓存直接发送到网络中(发送给不同的订阅者时,都可以使用同一个页面缓存),避免了重复复制操作。
如果有10个消费者,传统方式下,数据复制次数为4*10=40次,
而使用“零拷贝技术”只需要1+10=11次,一次为从磁盘复制到页面缓存,
10次表示10个消费者各自读取一次页面缓存。