-
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);
复制代码
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)
方法中的index
和length
去读取原内存 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();
}
}
ctx.write(raf.length());
if (ctx.pipeline().get(SslHandler.class) == null) {
//2.调用 raf.getChannel() 方法获取一个 FileChannel
//3.将FileChannel封装成一个DefaultFileRegion
ctx.write(new DefaultFileRegion(raf.getChannel(), 0, length));
} else {
ctx.write(new ChunkedFile(raf));
}
ctx.write(“\n”);
}
复制代码
总结
–
通过以上的介绍,相信小伙伴们对于Netty的零拷贝机制原理也有了一定的了解,有没有思考一个问题,当我们向缓冲区写入数据时,如果写入的数据超过设置的容量(capacity)怎么办?其实Netty 提供了动态扩容机制,有兴趣的小伙伴们可以自己去了解一下。
我们下节来讲讲Netty的引导程序的源码分析。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最后
我想问下大家当初选择做程序员的初衷是什么?有思考过这个问题吗?高薪?热爱?
既然入了这行就应该知道,这个行业是靠本事吃饭的,你想要拿高薪没有问题,请好好磨练自己的技术,不要抱怨。有的人通过培训可以让自己成长,有些人可以通过自律强大的自学能力成长,如果你两者都不占,还怎么拿高薪?
架构师是很多程序员的职业目标,一个好的架构师是不愁所谓的35岁高龄门槛的,到了那个时候,照样大把的企业挖他。为什么很多人想进阿里巴巴,无非不是福利待遇好以及优质的人脉资源,这对个人职业发展是有非常大帮助的。
如果你也想成为一名好的架构师,那或许这份Java核心架构笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。
中高级开发必知必会:
如果你两者都不占,还怎么拿高薪?
架构师是很多程序员的职业目标,一个好的架构师是不愁所谓的35岁高龄门槛的,到了那个时候,照样大把的企业挖他。为什么很多人想进阿里巴巴,无非不是福利待遇好以及优质的人脉资源,这对个人职业发展是有非常大帮助的。
如果你也想成为一名好的架构师,那或许这份Java核心架构笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。
中高级开发必知必会:
[外链图片转存中…(img-tm8GkGMF-1712099947382)]