This is the 11th day of my participation in the August More Text Challenge. For details, see:August is more challenging

preface

Once you know how byteBuffers work, it’s easy to understand Netty’s ByteBuf.

ByteBuf is a data buffer enclosed by Netty framework. Unlike position, LIMIT, flip and other properties and operations that control reading and writing ByteBuffer, ByteBuf uses two position Pointers to assist reading and writing buffer operations. The values are readIndex and writeIndex.

The following relationships exist between readIndex, writeIndex, and Capacity variables:

0 <= readIndex <= writeIndex <= capacity
Copy the code

Realize the principle of

When ByteBuffer is initialized, both readIndex and writeIndex start at 0. As shown in the figure below:

When data is written, writeIndex increases, as shown in the figure below:

When reading data, the readIndex will increase, but will not exceed the writeIndex, as shown in the figure below:

After a read, the area from index 0 to the readIndex position is considered discard bytes. This space can be freed by calling discardReadBytes, which acts like the Compact () method of ByteBuffer to remove unwanted data and reuse the buffer. The figure below shows what happens after discardReadBytes is implemented, which corresponds to more writable space.

The use case for ByteBuf

To better understand ByteBuf, write the following example:

public class ByteBufDemo {

	/ * * *@param args
	 */
	public static void main(String[] args) {
		// Create a buffer
		ByteBuf buffer = Unpooled.buffer(10);
		System.out.println("------------ initial buffer ------------");
		printBuffer(buffer);

		// Add some data to the buffer
		System.out.println("------------ Add data to buffer ------------");

		String s = "love";
		buffer.writeBytes(s.getBytes());
		printBuffer(buffer);

		// Read the data
		System.out.println("------------ Read data ------------");

		while (buffer.isReadable()) {
			System.out.println(buffer.readByte());
		}

		printBuffer(buffer);

		/ / compact execution
		System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- perform discardReadBytes -- -- -- -- -- -- -- -- -- -- -- --");
		buffer.discardReadBytes();
		printBuffer(buffer);

		The clear / / execution
		System.out.println("------------ Run clear to clear the buffer ------------");
		buffer.clear();
		printBuffer(buffer);

	}

	/** * Print out ByteBuf information **@param buffer
	 */
	private static void printBuffer(ByteBuf buffer) {
		System.out.println("readerIndex:" + buffer.readerIndex());
		System.out.println("writerIndex:" + buffer.writerIndex());
		System.out.println("capacity:"+ buffer.capacity()); }}Copy the code

Output results:

------------ Initial buffer ------------ readerIndex:0WriterIndex:0Capacity:10------------ Add data to buffer ------------ readerIndex:0WriterIndex:4Capacity:10------------ Read data ------------108
111
118
101ReaderIndex:4WriterIndex:4Capacity:10-- -- -- -- -- -- -- -- -- -- -- -- perform discardReadBytes -- -- -- -- -- -- -- -- -- -- -- -- readerIndex:0WriterIndex:0Capacity:10------------ Run clear to clear the buffer ------------ readerIndex:0WriterIndex:0Capacity:10

Process finished with exit code 0
Copy the code

As you can see from comparing the ByteBuffer and ByteBuf examples, Netty provides a much easier tool (unpooled) to create a ByteBuf without having to execute the flip() method to switch between read and write modes. ByteBuf, by contrast, is much easier to use.

Three usage modes of ByteBuf

ByteBuf can be used in three modes: Heap Buffer, Direct Buffer, and Composite Buffer.

Heap buffer mode

The heap buffer pattern is also known as the support array, where the data is the heap space that resides in the JVM and is implemented by storing the data in an array.

Advantages: Data stored in the JVM heap can be quickly created and released, and provides a fast method of data access;

Disadvantages: Data needs to be copied to the direct buffer each time it is transferred with I/O.

Here is an example code for the heap buffer:

public class ByteBufHeapBufferDemo {

	/ * * *@param args
	 */
	public static void main(String[] args) {
		
		// Create a heap buffer
		ByteBuf buffer = Unpooled.buffer(10);
		String s = "waylau";
		buffer.writeBytes(s.getBytes());

		// Check whether the array is supported
		if (buffer.hasArray()) {

			// Get a reference to the supporting array
			byte[] array = buffer.array();

			// Calculate the offset of the first byte
			int offset = buffer.readerIndex() + buffer.arrayOffset();

			// Number of readable bytes
			intlength = buffer.readableBytes(); printBuffer(array, offset, length); }}/** * Print Buffer information **@param buffer
	 */
	private static void printBuffer(byte[] array, int offset, int len) {
		System.out.println("array:" + array);
		System.out.println("Array - > String:" + new String(array));
		System.out.println("offset:" + offset);
		System.out.println(Len: ""+ len); }}Copy the code

Output results:

Array: [B@5b37e0d2 array->String: waylau offset:0Len:6

Process finished with exit code 0
Copy the code

Direct buffer mode

Direct buffers are direct memory allocated out of the heap and do not occupy heap space.

Advantages: Good performance when using sockets to transfer data, avoiding the process of copying data from the JVM heap memory to the direct buffer, improving performance.

Disadvantages: Direct buffers are more expensive to allocate memory space and free than heap buffers.

Direct buffers are recommended for reads and writes that involve a large amount of I/O data. For back-end business message codec modules, it is recommended to use heap buffers.

Here is an example code for the direct buffer:

public class ByteBufDirectBufferDemo {

	/ * * *@param args
	 */
	public static void main(String[] args) {

		// Create a direct buffer
		ByteBuf buffer = Unpooled.directBuffer(10);
		String s = "waylau";
		buffer.writeBytes(s.getBytes());

		// Check whether the array is supported.
		// If it is not a support array, it is a direct buffer
		if(! buffer.hasArray()) {// Calculate the offset of the first byte
			int offset = buffer.readerIndex();

			// Number of readable bytes
			int length = buffer.readableBytes();

			// Get the byte content
			byte[] array = new byte[length]; buffer.getBytes(offset, array); printBuffer(array, offset, length); }}/** * Print Buffer information **@param buffer
	 */
	private static void printBuffer(byte[] array, int offset, int len) {
		System.out.println("array:" + array);
		System.out.println("Array - > String:" + new String(array));
		System.out.println("offset:" + offset);
		System.out.println(Len: ""+ len); }}Copy the code

Output results:

Array: [B@6d5380c2 array->String: waylau offset:0Len:6

Process finished with exit code 0
Copy the code

Compound buffer mode

Compound buffers are Netty – specific buffers. Essentially similar to providing a composite view of one or more ByteBuFs, different types of ByteBuFs can be added and removed as needed.

Advantages: Provides an access method for users to freely combine multiple Bytebufs, avoiding copying and allocating new buffers.

Disadvantages: Access to its supporting array is not supported. So if you want to access it, you need to copy the content into the heap memory before accessing it.

The following example is a composite buffer that combines the heap buffer and the direct buffer together without any copying process, just creating a view.

public class ByteBufCompositeBufferDemo {

	/ * * *@param args
	 */
	public static void main(String[] args) {

		// Create a heap buffer
		ByteBuf heapBuf = Unpooled.buffer(3);
		String way = "way";
		heapBuf.writeBytes(way.getBytes());

		// Create a direct buffer
		ByteBuf directBuf = Unpooled.directBuffer(3);
		String lau = "lau";
		directBuf.writeBytes(lau.getBytes());

		// Create a composite buffer
		CompositeByteBuf compositeBuffer = Unpooled.compositeBuffer(10);
		compositeBuffer.addComponents(heapBuf, directBuf); // Add the buffer to the matching buffer

		// Check whether the array is supported.
		// If it is not a support array, it is a compound buffer
		if(! compositeBuffer.hasArray()) {for (ByteBuf buffer : compositeBuffer) {
				// Calculate the offset of the first byte
				int offset = buffer.readerIndex();

				// Number of readable bytes
				int length = buffer.readableBytes();

				// Get the byte content
				byte[] array = new byte[length]; buffer.getBytes(offset, array); printBuffer(array, offset, length); }}}/** * Print Buffer information **@param buffer
	 */
	private static void printBuffer(byte[] array, int offset, int len) {
		System.out.println("array:" + array);
		System.out.println("Array - > String:" + new String(array));
		System.out.println("offset:" + offset);
		System.out.println(Len: ""+ len); }}Copy the code

Output results:

Array: [B@4d76f3f8 array->String: way offset:0Len:3Array: [B@2d8e6db6 array-> array: lau offset:0Len:3

Process finished with exit code 0
Copy the code

CompositeByteBuf is a virtual buffer used to display multiple buffers as a single merge buffer, similar to a view in a database.

conclusion

Through the above introduction of ByteBuf, I believe that small partners have a certain understanding of the principle of ByteBuf. In the next section we dig deeper into the Netty source code.

At the end

I am a code is being hit is still trying to advance. If the article is helpful to you, remember to like, follow yo, thank you!