java-nio-zero-copy

【零拷贝在 Java 中的具体 API】

这里不再赘述 零拷贝 定义的相关内容,感兴趣可以查看 维基百科 - 零拷贝

Java 原生

在 Java 中,零拷贝具体的 API 指的是

java.nio.channels

    FileChannel#transferTo(long position, long count, WritableByteChannel target);
    FileChannel#transferFrom(ReadableByteChannel src, long position, long count)

其中的 WritableByteChannelReadableByteChannel 可以使用 java.nio.channels.Channels#newChannel注意这里是 Channels 后面有 s 是工具类) 从 OutputStreamInputStream 中获取。

Netty 中使用

// 接口
io.netty.channel.FileRegion
// 实现,把原生 FileChannel 包装下使用
io.netty.channel.DefaultFileRegion

Handler 直接 write 出去 FileRegion 类型的对象就可以,netty 会主动判断类型,如果是 FeilRegion 会做相应处理。

io.netty.channel.nio.AbstractNioByteChannel#

doWriteInternal(ChannelOutboundBuffer in, Object msg) {

    if (msg instanceof ByteBuf) {
        // ...
    } else if (msg instanceof FileRegion) {

        FileRegion region = (FileRegion) msg;
        if (region.transferred() >= region.count()) {
            in.remove();
            return 0;
        }

        // 这个地方调用的 doWriteFileRegion 就是了
        long localFlushedAmount = doWriteFileRegion(region);
        if (localFlushedAmount > 0) {
            in.progress(localFlushedAmount);
            if (region.transferred() >= region.count()) {
                in.remove();
            }
            return 1;
        }

    } else {
        // Should not reach here.
        throw new Error();
    }
    return WRITE_STATUS_SNDBUF_FULL;
}

// 最终调用到
io.netty.channel.socket.nio.NioSocketChannel#

doWriteFileRegion(FileRegion region) {
    final long position = region.transferred();
    // 这里的 transferTo 就是了
    return region.transferTo(javaChannel(), position);
}

可点击查看 Netty 关于 FileRegion 的详细文档。

问题

  1. Windows 下大文件,效果可能不佳,所以在 windows 上写 demo 程序可能得出截然相反的结果。

  2. Windows 下直接使用 FileChannel 传输大于 8M 的文件,需要多次传输。

    • Linux 中无此问题
    • 如果直接使用 netty 中的 FileRegion 无需关心此问题,其通过 transferred 字段记录了 position 位置。

windows 中使用原生 Channel 记录传输位置的方案

/**
 * Transfers bytes from origin channel to the given writable byte
 * channel.
 *
 * @param origin The origin channel
 * @param target The target channel
 * @return amount The number of bytes that were actually transferred
 * @throws IOException If some I/O error occurs
 */
public static long transferTo(FileChannel origin, WritableByteChannel target) 
        throws IOException {

    long size = origin.size();
    long transferred = 0;

    while (transferred < size) {
        transferred += origin.transferTo(transferred, size, target);
    }

    return transferred;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lixifun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值