写点什么

Netty 源码分析系列(八)Netty 如何实现零拷贝,圣思园张龙百度云

作者:MySQL神话
  • 2021 年 11 月 27 日
  • 本文字数:2590 字

    阅读完需:约 8 分钟

try {


FileChannel fc = new RandomAccessFile(file, "rw").getChannel();


MappedByteBuffer map = fc.map(FileChannel.MapMode.READ_WRITE, 0, file.length());


map.put("jiangwang".getBytes());


fc.position(file.length());


map.clear();


fc.write(map);


} catch (IOException e) {


e.printStackTrace();


}


复制代码


上述示例中,通过FileChannel.map()方法来创建MappedByteBuffer,该方法底层就是调用 Linux 的 mmap()实现的。


该方法将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射。这种方式适合读取大文件,同时也能对文件内容进行更改,但是如果其后要通过SocketChannel发送,还是需要 CPU 进行数据的拷贝。


使用 MappedByteBuffer,如果是小文件,执行效率不高;而且MappedByteBuffer只能通过调用FileChannelmap()取得,再没有其他方式。因此,Java 中设计MappedByteBuffer就是为大文件准备的。

2、Java 提供 sendfile 方式

Java FileChannel.transferTo() 底层实现就是通过 Linux 的 sendfile实现的。该方法直接将当前通道内容传输到另一个通道,没有涉及Buffer的任何操作。


以下是FileChannel.transferTo() 的使用示例:


//使用 sendfile:读取磁盘文件,并网络发送


FileChannel sourceChannel = new RandomAccessFile(source, "rw").getChannel();


SocketChannel socketChannel = SocketChannel.open(sa);


sourceChannel.transferTo(0, sourceChannel.size(), socketChannel);


复制代码


Netty 实现零拷贝




Netty 中的零拷贝的实现是基于 Java 的,换言之,底层也是基于操作系统实现的。相对于 Java 中的零拷贝而言,Netty 的零拷贝更多的是偏向于优化数据操作的概念。


Netty 中的零拷贝体现在以下几个方面:


  • Netty 提供了CompositeByteBuf类,它可以将多个ByteBuf合并为一个逻辑上的ByteBuf,避免了各个ByteBuf之间的复制。

  • 通过wrap操作,可以将 byte [] 数组、ByteBuf、ByteBuffer 等包装成一个 Netty ByteBuf 对象,进而避免了复制操作。

  • ByteBuf支持slice操作,因此可以将ByteBuf分解为多个共享同一个存储区域的ByteBuf,避免了内存的复制。

  • 通过FileRegion包装的FileChannel.transferTo()实现文件传输,可以直接将文件缓冲区的数据发送到目标Channel,避免了通过循环 while方式导致的内存复制问题。


从上面几个方法可以看出,前三个方法都是广义零拷贝,其实现方式都是为了减少不必要的数据复制,偏向于应用层数据优化操作。而第四个方法,FileRegion


包装的FileChannel.transferTo(),才是真正的零拷贝(狭义零拷贝)。


下面分别来看其每一种实现。

1、CompositeByteBuf 方式

CompositeByteBuf 将多个ByteBuf合并为一个逻辑上的ByteBuf,类似于用一个链表,把分散的多个ByteBuf通过引用连接起来。分散的多个ByteBuf在内存中可能是大小各异、互不相连的区域,通过链表串联起来,作为一块逻辑上的大区域。而在实际数据读取时,还是会去各自每一小块上读取。


下图展示了 CompositeByteBuf 的原理:



以下是 CompositeByteBuf 使用的代码示例:


ByteBuf header = ...


ByteBuf body = ...


CompositeByteBuf compositeBuffer = Unpooled.compositeBuffer();


compositeBuffer.addComponents(true, header,body);


《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享


复制代码

2、wrap 方式

可以通过 wrap 操作来实现零拷贝。


通过 wrap 操作,可以将 byte [] 数组、ByteBuf、ByteBuffer 等包装成一个 Netty ByteBuf 对象。


例如,通过 Unpooled.wrappedBuffer方法来将 bytes 包装成为一个UnpooledHeapByteBuf对象,而在包装的过程中,是不会有复制操作的。即最后生成的 ByteBuf 对象是和 bytes 数组共用了同一个存储空间,对 bytes 的修改也会反映到 ByteBuf 对象中。


以下是Unpooled.wrappedBuffer使用的代码示例:


ByteBuf header = ...


ByteBuf body = ...


ByteBuf allByteBuf = Unpooled.wrappedBuffer(header,body);


复制代码

3、slice 方式

可以通过 slice 方式实现零拷贝,原理图如下:



通过 Slice 操作,将ByteBuf分解为多个共享同一个存储区域的ByteBuf。slice 恰好是将一整块区域,划分成逻辑上的独立小区域,在读取每个逻辑上的小区域时,实际会去按 slice(int index,int length)方法中的indexlength去读取原内存 buffer 的数据。


以下是 slice 使用的示例代码:


ByteBuf bytebuf = ...


ByteBuf header = bytebuf.slice(0,5);


ByteBuf body = bytebuf.slice(5,10);


复制代码

4、 FileRegion 方式

FileRegion底层包装的是 Java 的FileChannel.transferTo()实现文件传输,因此可以直接将文件缓冲区的数据发送到目标Channel。这种方式才是真正操作系统级别的零拷贝。


以下是FileRegion使用的代码示例:


public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {


RandomAccessFile raf = null;


long length = 0;


try {


//1.通过 RandomAccessFile 打开一个文件


raf = new RandomAccessFile(msg, "r");


length = raf.length();


} catch (Exception e) {


ctx.writeAndFlush("ERR:" + e.getClass() + ": " + e.getMessage());


return;


} finally {


if (length < 0 & raf != null) {


raf.close();

写在最后

作为一名即将求职的程序员,面对一个可能跟近些年非常不同的 2019 年,你的就业机会和风口会出现在哪里?在这种新环境下,工作应该选择大厂还是小公司?已有几年工作经验的老兵,又应该如何保持和提升自身竞争力,转被动为主动?


就目前大环境来看,跳槽成功的难度比往年高很多。一个明显的感受:今年的面试,无论一面还是二面,都很考验 Java 程序员的技术功底。


最近我整理了一份复习用的面试题及面试高频的考点题及技术点梳理成一份“Java 经典面试问题(含答案解析).pdf 和一份网上搜集的“Java 程序员面试笔试真题库.pdf”(实际上比预期多花了不少精力),包含分布式架构、高可扩展、高性能、高并发、Jvm 性能调优、Spring,MyBatis,Nginx 源码分析,Redis,ActiveMQ、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx 等多个知识点高级进阶干货!


由于篇幅有限,为了方便大家观看,这里以图片的形式给大家展示部分的目录和答案截图!


Java 经典面试问题(含答案解析)

阿里巴巴技术笔试心得


本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

用户头像

MySQL神话

关注

还未添加个人签名 2021.11.12 加入

还未添加个人简介

评论

发布
暂无评论
Netty 源码分析系列(八)Netty 如何实现零拷贝,圣思园张龙百度云