【Netty】字节缓冲区 ByteBuf(七)(下)

news2025/1/10 12:11:35

文章目录

  • 前言
  • 一、实现原理
  • 二、ByteBuf 的使用案例
  • 三、ByteBuf 的3种使用模式
    • 3.1 堆缓冲模式
    • 3.2 直接缓冲区模式
    • 3.3 复合缓冲区模式
  • 总结

前言

回顾Netty系列文章:

  • Netty 概述(一)
  • Netty 架构设计(二)
  • Netty Channel 概述(三)
  • Netty ChannelHandler(四)
  • ChannelPipeline源码分析(五)
  • 字节缓冲区 ByteBuf (六)(上)

在了解了 ByteBuffer 的原理之后,再来理解Netty 的 ByteBuf 就比较简单了。

ByteBuf 是 Netty 框架封装的数据缓冲区,区别于 position、limit、flip等属性和操作来控制 ByteBuffer 的读写,ByteBuf 通过两个位置指针来协助缓冲区的读写操作,分别是readIndex和writeIndex。

readIndex、writeIndex和capacity变量存在以下关系:

0 <= readIndex <= writeIndex <= capacity

一、实现原理

初始化 ByteBuffer 时,readIndex 和 writeIndex 取值一开始都是0。如下图所示:
在这里插入图片描述

当执行写入数据之后,writeIndex会增加,如下图所示:
在这里插入图片描述

当执行读入数据之后则会使readIndex增加,但不会超过writeIndex,如下图:
在这里插入图片描述

在读取之后,索引 0 到 readIndex位置的区域被视为废弃字节(discard)。可以调用discardReadBytes方法,来释放这部分空间,其作用类似于 ByteBuffer的compact()方法,移除无用的数据,实现缓冲区的重复利用。如下图,展示了执行discardReadBytes后的情况,相当于可写的空间变大了。
在这里插入图片描述

二、ByteBuf 的使用案例

为了更好的理解ByteBuf,编写了以下示例:

public class ByteBufDemo {


	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// 创建一个缓冲区
		ByteBuf buffer = Unpooled.buffer(10);
		System.out.println("------------初始时缓冲区------------");
		printBuffer(buffer);

		// 添加一些数据到缓冲区中
		System.out.println("------------添加数据到缓冲区------------");

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

		// 读取数据
		System.out.println("------------读取数据------------");

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

		printBuffer(buffer);

		// 执行compact
		System.out.println("------------执行discardReadBytes------------");
		buffer.discardReadBytes();
		printBuffer(buffer);

		// 执行clear
		System.out.println("------------执行clear清空缓冲区------------");
		buffer.clear();
		printBuffer(buffer);

	}

	/**
	 * 打印出ByteBuf的信息
	 * 
	 * @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());
	}
}

输出结果:

-----------初始时缓冲区------------
readerIndex:0
writerIndex:0
capacity:10
------------添加数据到缓冲区------------
readerIndex:0
writerIndex:4
capacity:10
------------读取数据------------
108
111
118
101
readerIndex:4
writerIndex:4
capacity:10
------------执行discardReadBytes------------
readerIndex:0
writerIndex:0
capacity:10
------------执行clear清空缓冲区------------
readerIndex:0
writerIndex:0
capacity:10

Process finished with exit code 0

对比ByteBuffer和ByteBuf两个示例可以看出,Netty 提供了更加方便地创建ByteBuf的工具(unpooled),同时,也不必再执行flip()方法来切换读写模式。对比而言,ByteBuf更加易于使用。

三、ByteBuf 的3种使用模式

ByteBuf 共有三种使用模式:堆缓冲区模式(Heap Buffer)、直接缓冲区模式(Direct Buffer)和 复合缓冲区模式(Composite Buffer)。

3.1 堆缓冲模式

堆缓冲区模式又称为支撑数组,其数据是存放在JVM的堆空间,通过将数据存储在数组中实现。

优点:数据存储在JVM堆中可以快速的创建和快速释放,并且提供了数据快速访问的方法;
缺点:每次数据与 I/O 进行传输时,都需要将数据复制到直接缓冲区。

以下是堆缓冲区的代码示例:

public class ByteBufHeapBufferDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		// 创建一个堆缓冲区
		ByteBuf buffer = Unpooled.buffer(10);
		String s = "waylau";
		buffer.writeBytes(s.getBytes());

		// 检查是否是支撑数组
		if (buffer.hasArray()) {

			// 获取支撑数组的引用
			byte[] array = buffer.array();

			// 计算第一个字节的偏移量
			int offset = buffer.readerIndex() + buffer.arrayOffset();

			// 可读字节数
			int length = buffer.readableBytes();
			printBuffer(array, offset, length);
		}
	}

	/**
	 * 打印出Buffer的信息
	 * 
	 * @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);
	}
}

输出结果:

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

Process finished with exit code 0

3.2 直接缓冲区模式

直接缓冲区属于堆外分配的直接内存,不会占用堆得空间。

优点:使用 socket 传输数据时性能很好,避免了数据从 JVM 堆内存复制到直接缓冲区的过程,提高了性能。
缺点:相对于堆缓冲区而言,直接缓冲区分配内存空间和释放更为昂贵。
对于涉及大量的 I/O 数据的读写,建议使用直接缓冲区。而对于用于后端业务消息编解码模块,建议使用堆缓冲区。
以下是直接缓冲区代码示例:

public class ByteBufDirectBufferDemo {

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

		// 创建一个直接缓冲区
		ByteBuf buffer = Unpooled.directBuffer(10);
		String s = "waylau";
		buffer.writeBytes(s.getBytes());

		// 检查是否是支撑数组.
		// 不是支撑数组,则为直接缓冲区
		if (!buffer.hasArray()) {

			// 计算第一个字节的偏移量
			int offset = buffer.readerIndex();

			// 可读字节数
			int length = buffer.readableBytes();

			// 获取字节内容
			byte[] array = new byte[length];
			buffer.getBytes(offset, array);

			printBuffer(array, offset, length);
		}
	}

	/**
	 * 打印出Buffer的信息
	 * 
	 * @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);
	}
}

输出结果:

java复制代码array:[B@6d5380c2
array->String:waylau
offset:0
len:6

Process finished with exit code 0

3.3 复合缓冲区模式

复合缓冲区是 Netty 特有的缓冲区。本质上类似于提供一个或多个 ByteBuf 的组合视图,可以根据需要添加和删除不同类型的 ByteBuf。

优点:提供了一种访问方式让使用者自由地组合多个ByteBuf,避免了复制和分配新的缓冲区。
缺点:不支持访问其支撑数组。因此如果要访问,需要先将内容复制到堆内存中,再进行访问。
以下示例是复合缓冲区将堆缓冲区和直接缓冲区组合在一起,没有进行任何复制过程,仅仅创建了一个视图而已。

public class ByteBufCompositeBufferDemo {

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

		// 创建一个堆缓冲区
		ByteBuf heapBuf = Unpooled.buffer(3);
		String way = "way";
		heapBuf.writeBytes(way.getBytes());

		// 创建一个直接缓冲区
		ByteBuf directBuf = Unpooled.directBuffer(3);
		String lau = "lau";
		directBuf.writeBytes(lau.getBytes());

		// 创建一个复合缓冲区
		CompositeByteBuf compositeBuffer = Unpooled.compositeBuffer(10);
		compositeBuffer.addComponents(heapBuf, directBuf); // 将缓冲区添加到符合缓冲区

		// 检查是否是支撑数组.
		// 不是支撑数组,则为复合缓冲区
		if (!compositeBuffer.hasArray()) {

			for (ByteBuf buffer : compositeBuffer) {
				// 计算第一个字节的偏移量
				int offset = buffer.readerIndex();

				// 可读字节数
				int length = buffer.readableBytes();

				// 获取字节内容
				byte[] array = new byte[length];
				buffer.getBytes(offset, array);

				printBuffer(array, offset, length);
			}

		}
	}

	/**
	 * 打印出Buffer的信息
	 * 
	 * @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);
	}
}

输出结果:

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

Process finished with exit code 0

CompositeByteBuf是一个虚拟的缓冲区,其用途是将多个缓冲区显示为单个合并缓冲区,类似数据库中的视图。

总结

通过以上对于ByteBuf的介绍,相信小伙伴们对于ByteBuf的原理也有了一定的了解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/567738.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙Hi3861问题解决-DevEco VSCode无法跳转

一、问题描述 使用Windows和Ubuntu混合编译下载&#xff0c;在windows下搭建VSCodeDevEco Device Tool环境。通过SSH远程Ubuntu系统进行代码修改与编译。 在VSCode中&#xff0c;Ctrl左键&#xff0c;无法跳转。按住Ctrl键&#xff0c;鼠标移到函数上&#xff0c;也不会有任何反…

Flink+hadoop部署及Demo

Hadoop集群高可用部署 下载hadoop包地址 https://dlcdn.apache.org/hadoop/common/hadoop-3.2.4/hadoop-3.2.4.tar.gz 上传并解压到3台服务器 配置3台主机的hosts和免密登录 1.修改.bash_profile vi .bash_profile # HADOOP_HOME export HADOOP_HOME/apps/svr/hadoop-3.2.…

一分钟掌握技术术语:API(接口)|电商平台API接口展示示例

很多产品经理在项目开发过程中经常听到&#xff1a;你调我这个接口就好了&#xff1b;这个功能你写个接口给我&#xff1b;有什么不懂的就看下API接口文档。 开发经常说的接口是什么意思呢&#xff1f;术语解释&#xff1a;API&#xff08;Application Programming Interface&a…

GPT虚拟直播Demo系列(一)|GPT接入直播间实现主播与观众互动

摘要 ChatGPT和元宇宙都是当前数字化领域中非常热门的技术和应用。结合两者的优势和特点&#xff0c;可以探索出更多的应用场景和商业模式。例如&#xff0c;在元宇宙中使用ChatGPT进行自然语言交互&#xff0c;可以为用户提供更加智能化、个性化的服务和支持&#xff1b;在Ch…

成功拿到B站测开岗年薪50W+offer,面经分享

一面 首先&#xff0c;自我介绍&#xff0c;我介绍了自己的技术栈和项目。 技术栈提到过Spring、Redis、Kafka、Docker、K8s、大数据。 项目提到过接口和UI自动化。 我有个大数据平台项目&#xff0c;问了比较多&#xff0c;聊着聊着&#xff0c;提到自己研究过Selenium、T…

Vue3:组件基础(上)

Vue3&#xff1a;组件基础&#xff08;上&#xff09; Date: April 11, 2023 Sum: vite的基本使用、组件化开发思想、vue组件的构成、组件的基本使用、封装组件的案例 单页面应用程序 什么是单页面应用程序 单页面应用程序&#xff08;英文名&#xff1a;Single Page Applic…

【网络安全篇】如何当着面试官的面优雅地装逼!

从胡子的长度和忧郁的眼神我察觉到&#xff0c;面前坐着的这位面试官应该有点东西。 浑身上下流露着打过CTF的气场。我像以往一样&#xff0c;准备花3分钟的时间进行自我介绍。 在此期间&#xff0c;面试官面无表情但很有耐心的听着我balabala。 我按照原定计划顺利地介绍(吹…

国内行业垂直型SaaS公司有哪些?发展前景如何?

01 国内行业垂直型SaaS公司有哪些&#xff1f; 根据艾瑞咨询测算&#xff0c;2021年中国企业级应用软件市场规模达到2592亿元&#xff0c;SaaS在其中占比达到28.1%。 在企业数字化转型的全景图中&#xff0c;SaaS扮演着应用场景层面的关键作用&#xff0c;往往是企业特定环节数…

什么是护网?护网怎么参加?

一、什么是护网行动&#xff1f; 护网行动是以公安部牵头的&#xff0c;用以评估企事业单位的网络安全的活动。 具体实践中。公安部会组织攻防两方&#xff0c;进攻方会在一个月内对防守方发动网络攻击&#xff0c;检测出防守方&#xff08;企事业单位&#xff09;存在的安全…

智慧水务系统如何进行有效的数据架构整改?三个企业的改造实践分享

在智慧水务系统中&#xff0c;往往需要对设备中产生的液位、电流、水量等实时指标数据进行存储、分析及监控操作&#xff0c;而这些都是典型的时序数据。面对这些数据的处理时&#xff0c;很多企业在前期选择的大都是传统的实时数据库甚至关系型数据库&#xff0c;随着设备数量…

字段信息 详解,以易举例,创建数据库,程序自动创建数据库的前提,程序读写数据库的第一步

今天要做一个处理比较多数据的工具&#xff0c;就是桌面小软件&#xff0c;重新收拾起以前的易语言来编写&#xff0c;C#等也可以&#xff0c;反正就是最后的成品是绿色免安装。 数据多&#xff0c;优先考虑的就是数据库操作了&#xff0c;又快又好是吧&#xff1f; 第一步&am…

MyBatis源码学习五之插件

MyBatis源码学习五之插件 官网MyBatis插件介绍&#xff1a;https://mybatis.org/mybatis-3/zh/configuration.html#plugins MyBatis的插件使用的范围比较广&#xff0c;像PageHelper就是利用的插件的原理去实现的。插件会做一些通用的功能&#xff0c;比如打印日志&#xff0…

行业报告 | 2022文化科技十大前沿应用趋势(下)

原创 | 文 BFT机器人 04 商业创新 趋势7&#xff1a;区块链技术连接传统文化&#xff0c;数字藏品市场在探索中发展 核心内容&#xff1a; 2022年&#xff0c;数字藏品在区块链技术的助力下应运而生。狭义的数字藏品是指使用区块链技术、基于特定的文化资源所生成唯一的数字凭…

Linux学习记录——이십사 多线程(1)

文章目录 1、以Linux角度理解2、并不是所有的操作系统都这样管理3、页表和物理内存4、线程优缺点5、进程和线程的区别6、线程接口1、pthread_create.2、pthread_join3、线程终止取消正在终止的线程 4、线程分离 1、以Linux角度理解 创建一个进程时&#xff0c;会有pcb结构体&a…

Java集合回顾

能不能和你竭尽全力奔跑 / 向着海平线 / 余晖消逝之前都不算终点 文章目录 集合概述Java 集合概览List, Set, Queue, Map 四者的区别&#xff1f;集合框架底层数据结构总结如何选用集合?为什么要使用集合&#xff1f; ListArrayList 和 Array&#xff08;数组&#xff09;的区…

Java SpringBoot自动化网页爬虫项目

介绍 Java SpringBoot自动化网页爬虫&#xff0c;以图形化方式定义爬虫流程&#xff0c;不写代码即可完成爬虫。 平台以流程图的方式定义爬虫,是一个高度灵活可配置的爬虫平台 功能根据需要可定制化开发。 特性 支持Xpath/JsonPath/css选择器/正则提取/混搭提取 支持JSON/XML/二…

aop+springboot实现数据字典表

文章目录 概要整体架构流程目录结构方式pom文件信息application.yml文件信息aop实现方式(重点方式)我们这里主要的实现了&#xff0c;就是在前段请求数据的时候&#xff0c;我们利用aop&#xff0c;拦截数据&#xff0c;将code编码进行翻译&#xff0c;翻译的方式就是我们将cod…

LabVIEWCompactRIO 开发指南34 在模拟模式下调试

LabVIEWCompactRIO 开发指南34 在模拟模式下调试 在仿真模式下执行LabVIEW FPGA VI时&#xff0c;可以访问标准LabVIEW调试功能&#xff0c;包括突出显示执行、探测和断点。LabVIEW2013及更高版本包含了一个额外的调试工具&#xff0c;称为采样探针。在仿真中运行时插入FPGA设…

U盘超级加密3000试用版与正式版的区别有哪些?

U盘超级加密3000是一款专业的U盘加密软件&#xff0c;它可以为U盘、移动硬盘、内存卡等移动存储设备加密。软件拥有正式版和试用版&#xff0c;那么这两个版本有什么区别呢&#xff1f;下面我们就一起来了解一下。 U盘超级加密3000试用版和正式版的区别 打开软件时的区别 试用…

C++第三章:字符串、向量和数组

字符串、向量和数组 一、命名空间的using声明每个名字独立using声明头文件不应包含using声明 二、标准库类型string2.1 定义和初始化string对象直接初始化和拷贝初始化 2.2 string对象上的操作读写string对象读取未知数量的string对象使用getline读取一整行string的empty和size…