Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the understanding of zero copy of Netty ByteBuf

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces how to understand the zero copy of Netty ByteBuf. The content is very detailed. Interested friends can use it for reference. I hope it will be helpful to you.

According to Wiki's definition of Zero-copy:

"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network.

The so-called Zero-copy means that when manipulating data, there is no need to copy data buffer from one memory area to another. Because there is one less copy of memory, the efficiency of CPU is improved.

Zero-copy at the OS level usually refers to avoiding copying data back and forth between User-space and Kernel-space. For example, the mmap system call provided by Linux can map a section of user space memory to kernel space. When the mapping is successful, users' modifications to this memory area can be directly reflected to kernel space; similarly, kernel space modifications to this area can also directly reflect user space. Because of this mapping relationship, we do not need to copy data between user state (User-space) and kernel state (Kernel-space), which improves the efficiency of data transmission.

It should be noted that the Zero-copy in Netty is different from the Zero-copy mentioned above at the OS level. Netty's Zero-coyp is completely in the user mode (Java level), and its Zero-copy is more inclined to the concept of optimizing data operations.

The Zero-copy of Netty is reflected in the following aspects:

Netty provides the CompositeByteBuf class, which can merge multiple ByteBuf into a logical ByteBuf, avoiding copying between ByteBuf.

Through wrap operation, we can wrap byte [] array, ByteBuf, ByteBuffer and so on into a Netty ByteBuf object, thus avoiding copy operation.

ByteBuf supports slice operation, so ByteBuf can be decomposed into multiple ByteBuf that share the same storage area, avoiding memory copy.

The file transfer is realized through the FileChannel.tranferTo packaged by FileRegion, and the data of the file buffer can be sent directly to the target Channel, which avoids the memory copy problem caused by the traditional circular write.

Let's take a brief look at these common zero-copy operations.

Zero copy through CompositeByteBuf

Suppose we have a protocol data that consists of a header and a message body, which are stored in two ByteBuf respectively, namely:

ByteBuf header =... ByteBuf body =...

In code processing, we usually want to merge header and body into a single ByteBuf to facilitate processing, so the usual practice is:

ByteBuf allBuf = Unpooled.buffer (header.readableBytes () + body.readableBytes ()); allBuf.writeBytes (header); allBuf.writeBytes (body)

As you can see, we copied both header and body into the new allBuf, which virtually added two additional data copy operations.

So is there a more efficient and elegant way to achieve the same goal? Let's take a look at how CompositeByteBuf implements such a requirement.

ByteBuf header =... ByteBuf body =... CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer (); compositeByteBuf.addComponents (true, header, body)

In the above code, we define a CompositeByteBuf object and then call the

Public CompositeByteBuf addComponents (boolean increaseWriterIndex, ByteBuf... Buffers) {.}

Method to merge header and body into a logical ByteBuf, that is:

It is important to note, however, that although it seems that CompositeByteBuf is made up of two ByteBuf, within CompositeByteBuf, both ByteBuf exist separately, and CompositeByteBuf is logically a whole.

The above CompositeByteBuf code is also worth noting that we call addComponents (boolean increaseWriterIndex, ByteBuf...) Buffers) to add two ByteBuf, of which the * * parameter is true, which means that when a new ByteBuf is added, the writeIndex of the CompositeByteBuf is automatically incremented.

If we call the

CompositeByteBuf.addComponents (header, body)

So in fact, the writeIndex of compositeByteBuf is still 0, so it is impossible for us to read the data from compositeByteBuf at this time.

In addition to using the CompositeByteBuf class directly above, we can also use the Unpooled.wrappedBuffer method, which encapsulates the CompositeByteBuf operation underneath, making it easier to use:

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

Realizing zero copy through wrap operation

For example, if we have a byte array and we want to convert it to a ByteBuf object for subsequent operations, the traditional approach is to copy the byte array into ByteBuf, that is:

Byte [] bytes =... ByteBuf byteBuf = Unpooled.buffer (); byteBuf.writeBytes (bytes)

Obviously there is an additional copy operation in this way. We can use the relevant methods of Unpooled to wrap the byte array and generate a new ByteBuf instance without the need for copy operation. The above code can be changed to:

Byte [] bytes =... ByteBuf byteBuf = Unpooled.wrappedBuffer (bytes)

As you can see, we use the Unpooled.wrappedBuffer method to wrap bytes into a UnpooledHeapByteBuf object, and there is no copy operation during the wrapping process. That is, * the generated ByteBuf object shares the same storage space as the bytes array, and the changes to bytes will also be reflected in the ByteBuf object.

The Unpooled utility class also provides many overloaded wrappedBuffer methods:

Public static ByteBuf wrappedBuffer (byte [] array) public static ByteBuf wrappedBuffer (byte [] array, int offset, int length) public static ByteBuf wrappedBuffer (ByteBuffer buffer) public static ByteBuf wrappedBuffer (ByteBuf buffer) public static ByteBuf wrappedBuffer (byte []. Arrays) public static ByteBuf wrappedBuffer (ByteBuf... Buffers) public static ByteBuf wrappedBuffer (ByteBuffer... Buffers) public static ByteBuf wrappedBuffer (int maxNumComponents, byte []... Arrays) public static ByteBuf wrappedBuffer (int maxNumComponents, ByteBuf... Buffers) public static ByteBuf wrappedBuffer (int maxNumComponents, ByteBuffer... Buffers)

These methods can wrap one or more buffer as a ByteBuf object, thus avoiding copy operations.

Realizing zero copy through slice operation

Slice operation is the opposite of wrap operation. Unpooled.wrappedBuffer can merge multiple ByteBuf into one, while slice operation can slice a ByteBuf into multiple ByteBuf objects that share a storage area.

ByteBuf provides two slice operation methods:

Public ByteBuf slice (); public ByteBuf slice (int index, int length)

The slice method without arguments is equivalent to the buf.slice (buf.readerIndex (), buf.readableBytes ()) call, which returns the slice of the readable part of the buf. The slice (int index, int length) method is relatively flexible. We can set different parameters to get the slices of different regions of buf.

The following example shows a simple use of the ByteBuf.slice method:

ByteBuf byteBuf =... ByteBuf header = byteBuf.slice (0,5); ByteBuf body = byteBuf.slice (5,10)

There is no copy operation in the process of generating header and body by slice method. Header and body objects share different parts of byteBuf storage space internally. That is:

Zero copy through FileRegion

FileRegion is used in Netty to achieve zero copy of file transfer, but at the bottom FileRegion depends on the zero copy function of Java NIO FileChannel.transfer.

First of all, let's start with the most basic Java IO. Suppose we want to implement the function of a file copy, then using the traditional way, we have the following implementation:

Public static void copyFile (String srcFile, String destFile) throws Exception {byte [] temp = new byte [1024]; FileInputStream in = new FileInputStream (srcFile); FileOutputStream out = new FileOutputStream (destFile); int length; while ((length = in.read (temp))! =-1) {out.write (temp, 0, length);} in.close (); out.close ();}

The above is a typical code implementation for reading and writing binaries. Needless to say, we all know that the above code constantly reads the fixed-length data from the source file to the temp array, and then writes the contents of temp to the destination file. This copy operation does not have much impact on small files, but if we need to copy large files, frequent memory copy operations consume a lot of system resources.

Let's take a look at how FileChannel using Java NIO achieves zero copy:

Public static void copyFileWithFileChannel (String srcFileName, String destFileName) throws Exception {RandomAccessFile srcFile = new RandomAccessFile (srcFileName, "r"); FileChannel srcFileChannel = srcFile.getChannel (); RandomAccessFile destFile = new RandomAccessFile (destFileName, "rw"); FileChannel destFileChannel = destFile.getChannel (); long position = 0; long count = srcFileChannel.size (); srcFileChannel.transferTo (position, count, destFileChannel);}

As you can see, after using FileChannel, we can directly copy (transferTo) the contents of the source file directly to the destination file without the need for an additional temporary buffer to avoid unnecessary memory operations.

With some of the above theoretical knowledge, let's take a look at how to use FileRegion to transfer a file with zero copy in Netty:

@ Override public void channelRead0 (ChannelHandlerContext ctx, String msg) throws Exception {RandomAccessFile raf = null; long length =-1; try {/ / 1. Open a file through RandomAccessFile. Raf = new RandomAccessFile (msg, "r"); length = raf.length ();} catch (Exception e) {ctx.writeAndFlush ("ERR:" + e.getClass (). GetSimpleName () + ":" + e.getMessage () +'\ n'); return;} finally {if (length < 0 & & raf! = null) {raf.close () }} ctx.write ("OK:" + raf.length () +'\ n'); if (ctx.pipeline (). Get (SslHandler.class) = = null) {/ / SSL not enabled-can use zero-copy file transfer. / / 2. Call raf.getChannel () to get a FileChannel. / / 3. Package FileChannel into a DefaultFileRegion ctx.write (new DefaultFileRegion (raf.getChannel (), 0, length));} else {/ / SSL enabled-cannot use zero-copy file transfer. Ctx.write (new ChunkedFile (raf));} ctx.writeAndFlush ("\ n");}

The above code is an example of Netty, whose source code is in netty/example/src/main/java/io/netty/example/file/FileServerHandler.java

As you can see, the first step is to open a file through RandomAccessFile, and then Netty uses DefaultFileRegion to encapsulate a FileChannel, that is:

New DefaultFileRegion (raf.getChannel (), 0, length)

When we have FileRegion, we can write the contents of the file directly to Channel through it, instead of copying the contents of the file to temporary buffer, and then writing buffer to Channel. Through such a zero-copy operation, there is no doubt that it is very helpful to transfer large files.

On the understanding of the zero copy of Netty ByteBuf is shared here, I hope that the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report