这个 native 函数(Java_sun_nio_ch_FileChannelImpl_transferTo0)同样位于 JDK 源码包下的 native/sun/nio/ch/FileChannelImpl.c 源文件里面 。
JNI 函数 Java_sun_nio_ch_FileChannelImpl_transferTo0() 基于条件编译对不同的系统进行预编译,下面是 JDK 基于 Linux 系统内核对 transferTo() 提供的调用封装 。
- #if defined(__linux__) || defined(__solaris__)
- #include <sys/sendfile.h>
- #elif defined(_AIX)
- #include <sys/socket.h>
- #elif defined(_ALLBSD_SOURCE)
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/uio.h>
- #define lseek64 lseek
- #define mmap64 mmap
- #endif
- JNIEXPORT jlong JNICALL
- Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
- jobject srcFDO,
- jlong position, jlong count,
- jobject dstFDO)
- {
- jint srcFD = fdval(env, srcFDO);
- jint dstFD = fdval(env, dstFDO);
- #if defined(__linux__)
- off64_t offset = (off64_t)position;
- jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count);
- return n;
- #elif defined(__solaris__)
- result = sendfilev64(dstFD, &sfv, 1, &numBytes);
- return result;
- #elif defined(__APPLE__)
- result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0);
- return result;
- #endif
- }
- #include <sys/sendfile.h>
- ssize_t sendfile64(int out_fd, int in_fd, off_t *offset, size_t count);
- out_fd:待写入的文件描述符 。
- in_fd:待读取的文件描述符 。
- offset:指定 in_fd 对应文件流的读取位置,如果为空,则默认从起始位置开始 。
- count:指定在文件描述符 in_fd 和 out_fd 之间传输的字节数 。
也就是说,sendfile64() 函数不仅可以进行网络文件传输,还可以对本地文件实现零拷贝操作 。
其它的零拷贝实现
Netty 零拷贝
Netty 中的零拷贝和上面提到的操作系统层面上的零拷贝不太一样, 我们所说的 Netty 零拷贝完全是基于(Java 层面)用户态的,它的更多的是偏向于数据操作优化这样的概念 。
具体表现在以下几个方面:
- Netty 通过 DefaultFileRegion 类对 java.nio.channels.FileChannel 的 tranferTo() 方法进行包装,在文件传输时可以将文件缓冲区的数据直接发送到目的通道(Channel) 。
- ByteBuf 可以通过 wrap 操作把字节数组、ByteBuf、ByteBuffer 包装成一个 ByteBuf 对象, 进而避免了拷贝操作 。
- ByteBuf 支持 Slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf,避免了内存的拷贝 。
- Netty 提供了 CompositeByteBuf 类,它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf,避免了各个 ByteBuf 之间的拷贝 。
RocketMQ 和 Kafka 对比
RocketMQ 选择了 mmap+write 这种零拷贝方式,适用于业务级消息这种小块文件的数据持久化和传输 。
而 Kafka 采用的是 Sendfile 这种零拷贝方式,适用于系统日志消息这种高吞吐量的大块文件的数据持久化和传输 。
但是值得注意的一点是,Kafka 的索引文件使用的是 mmap+write 方式,数据文件使用的是 Sendfile 方式 。
推荐阅读
- 淘宝从百万到千万级并发的14次服务端架构演进之路
- 几百万扔进水?买二手房千万避开这几类房源
- 分布式、高并发、多线程,这些概念还傻傻分不清吗?
- Java 并发编程:如何保证共享变量的原子性?
- 硬核!如何模拟 5w+ 的并发用户?
- 格鲁吉亚中国茶王刘峻周诞辰150周年之际获百万赔偿
- 安徽省科技厅强化科技支撑推进精准扶贫
- PHP导出百万条数据方法
- 重金寻人 , 你是我们要找的百万英雄吗 内含福利
- 带你深入了解高并发架构
