驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
在零拷贝上吹了牛逼的Netty
/  

在零拷贝上吹了牛逼的Netty

开篇

首先,原谅我做了标题党,但是内容还是值得一看的。

不少关于Netty的文章和视频都表达一个意思,Netty之所以牛逼其中一个原因就是因为它的零拷贝机制。

那么这个零拷贝到底是什么?根据我查阅资料,发现Netty零拷贝体现在下面3个核心技术点,我们一起来看看真相吧!

正文

文件传输

可以和Netty零拷贝,扯上关系的第一个零拷贝就是Linux的零拷贝。我们用一个典型的文件传输案例来聊聊!

w1.png

备用图片1

这个典型的案例中,经过了如下2步

  1. 文件通过内核空间到用户空间的读取
  2. 然后又通过用户空间写到了内核空间

这样没有问题,但是我们如果让这个数据不通过用户空间,直接在内核态进行数据填充了,效果是不是更好,看下图?

w2.png

备用图片2

上面这种数据不经过用户空间,直接在内核空间填充的技术,就是零拷贝

Linux是提供了这类技术的,具体实现也有很多,这不是本文的重点,有兴趣的请自行谷歌。

Java的NIO提供了一个API叫做transferTo,而Netty也就在FileRegion中简单的包裹了一下,具体可以参考该类io.netty.channel.DefaultFileRegion#transferTo

综上:关于文件的零拷贝,这不是Netty原创的零拷贝,还是要归功于NIO的封装,当然最终还是要归功于系统!

内存拷贝

第二个Netty零拷贝相关的就是避C-HeapJava-Heap的转换。

我们需要知道Java读取文件流或者网络流,其底层都是native的实现,就是说Java调用了C来实现的。

比如java.net.SocketInputStream#socketRead0

private native int socketRead0(FileDescriptor fd,
                                   byte b[], int off, int len,
                                   int timeout)
        throws IOException;

比如java.io.FileInputStream#read0

private native int read0() throws IOException;

这种API调用方式,势必会导致C-HeapJava-Heap的一层拷贝,这个是会耗费资源的。

NIO本身就实现在堆外的内存DirectByteBuffer,Netty的ByteBuf也就是在这个上面封装了一层。我们来看看Netty如何应用NIODirectByteBuffer来避免这一层C-Java的拷贝的。

内存池的ByteBuf创建的调用链路

io.netty.buffer.PooledByteBufAllocator#newDirectBuffer
  io.netty.buffer.UnsafeByteBufUtil#newUnsafeDirectByteBuf
     io.netty.buffer.UnpooledDirectByteBuf#UnpooledDirectByteBuf
  			io.netty.buffer.UnpooledDirectByteBuf#allocateDirect
  				java.nio.DirectByteBuffer#DirectByteBuffer(int)

综上:关于ByteBuffer的零拷贝,不是Netty原创还是要归功于NIO

CompositeByteBuf

这个才算是Netty在零拷贝上的真实体现,原创精品,哈哈!

Composite复合的意思,可以看出是复合ByteBuf,这是一个NIO的ByteBuffer的包装器。可以将实际上多个ByteBuf复合成逻辑上的一个ByteBuf

public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements Iterable < ByteBuf > {
	private Component[] components; // resized when needed
	private static final class Component {
		final ByteBuf srcBuf; // the originally added buffer
		final ByteBuf buf; // srcBuf unwrapped zero or more times
		//这里:构造函数
		Component(ByteBuf srcBuf, int srcOffset, ByteBuf buf, int bufOffset, int offset, int len, ByteBuf slice) {
			//....
		}
	}
}

关键信息如上

  • 其内部是一个Component[],每一个Component又包含了ByteBuf
  • 构造函数中的offset很明显就是访问对应ByteBuf时候的偏移量了

结语

Netty零拷贝上的原创应该仅仅就是CompositeByteBuf了,其他2处顶多是包装了下NIO的特性。

但是你不能怀疑Netty仍然是java用的最广泛的网络通信框架,绝对值得广大Javaer学习

骐骥一跃,不能十步。驽马十驾,功在不舍。