Netty ByteBuf 使用详解

news2024/12/27 13:27:03

文章目录

  • 1.概述
  • 2. ByteBuf 分类
  • 3. 代码实例
    • 3.1 常用方法
      • 3.1.1 创建ByteBuf
      • 3.1.2 写入字节
      • 3.1.3 扩容
        • 3.1.2.1 扩容实例
        • 3.1.2.2 扩容计算新容量代码
      • 3.1.4 读取字节
      • 3.1.5 标记回退
      • 3.1.6 slice
      • 3.1.7 duplicate
      • 3.1.8 CompositeByteBuf
      • 3.1.9 retain & release
        • 3.1.9.1 retain & release
        • 3.1.9.2 Netty TailContext release
    • 3.2 完整实例
  • 4. 参考文献

1.概述

ByteBuf 对字节进行操作

ByteBuf 四个基本属性:

  • readerIndex: 读指针,字节数组,读到哪了
  • writerIndex: 写指针,字节数组,写到哪了
  • maxCapacity:最大容量,字节数组最大容量
  • markedReaderIndex:标记读指针,resetReaderIndex方法可以把readerIndex修改为markedReaderIndex,回退重新读数据
  • markedWriterIndex: 标记写指针,resetReaderIndex方法可以把 writerIndex 修改为markedWriterIndex,回退重新写数据
public abstract class AbstractByteBuf extends ByteBuf {
    int readerIndex;
    int writerIndex;
    private int markedReaderIndex;
    private int markedWriterIndex;
    private int maxCapacity;
}

2. ByteBuf 分类

ByteBuf 分为

  • 直接内存或堆内存(Heap/Direct)
  • 池化 和 非池化(Pooled/Unpooled)和 操作方式是否安全 (Unsafe/非 Unsafe)

ByteBuf 创建可以基于直接内存或堆内存

  • 直接内存创建和销毁的代价昂贵,但读写性能高(少一次内存复制),适合配合池化功能一起用
  • 直接内存对 GC 压力小,因为这部分内存不受 JVM 垃圾回收的管理,但也要注意及时主动释放

ByteBuf 池化 和 非池化

  • 没有池化,则每次都得创建新的 ByteBuf 实例,这个操作对直接内存代价昂贵,就算是堆内存,也会增加 GC 压力
  • 有了池化,则可以重用池中 ByteBuf 实例,并且采用了与 jemalloc 类似的内存分配算法提升分配效率
  • 高并发时,池化功能更节约内存,减少内存溢出的可能

ByteBuf 操作方式是否安全 (Unsafe/非 Unsafe)

  • Unsafe:表示每次调用 JDK 的 Unsafe 对象操作物理内存,依赖 offset + index 的方式操作数据
  • 非 Unsafe:则不需要依赖 JDK 的 Unsafe 对象,直接通过数组下标的方式操作数据

3. 代码实例

3.1 常用方法

3.1.1 创建ByteBuf

创建ByteBuf , 默认都是池化的

        // 堆内存的ByteBuf
        ByteBuf bufferHeap = ByteBufAllocator.DEFAULT.heapBuffer();
        // 直接内存的ByteBuf
        ByteBuf bufferDirect = ByteBufAllocator.DEFAULT.directBuffer();
        System.out.println(bufferHeap);
        System.out.println(bufferDirect);

在这里插入图片描述

3.1.2 写入字节

        bufferHeap.writeBytes(new byte[]{1, 2, 3, 4});
        bufferDirect.writeBytes(new byte[]{1, 2, 3, 4});
        print("第一次写入", bufferHeap);
        print("第一次写入", bufferDirect);

在这里插入图片描述

3.1.3 扩容

3.1.2.1 扩容实例
  • 默认 256
  • 扩容加一倍
  • 到了4194304,每次+4194304
      for (int i = 0; i < 100; i++) {
            bufferHeap.writeBytes(new byte[]{1, 2, 3, 4});
            bufferDirect.writeBytes(new byte[]{1, 2, 3, 4});
        }
        print("批量写入&扩容", bufferHeap);
        print("批量写入&扩容", bufferDirect);

在这里插入图片描述

3.1.2.2 扩容计算新容量代码
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
    ObjectUtil.checkPositiveOrZero(minNewCapacity, "minNewCapacity");
    if (minNewCapacity > maxCapacity) {
        throw new IllegalArgumentException(String.format("minNewCapacity: %d (expected: not greater than maxCapacity(%d)", minNewCapacity, maxCapacity));
    } else {
        int threshold = 4194304;
        if (minNewCapacity == 4194304) {
            return 4194304;
        } else {
            int newCapacity;
            if (minNewCapacity > 4194304) {
                newCapacity = minNewCapacity / 4194304 * 4194304;
                if (newCapacity > maxCapacity - 4194304) {
                    newCapacity = maxCapacity;
                } else {
                    newCapacity += 4194304;
                }

                return newCapacity;
            } else {
                for(newCapacity = 64; newCapacity < minNewCapacity; newCapacity <<= 1) {
                }

                return Math.min(newCapacity, maxCapacity);
            }
        }
    }
}

3.1.4 读取字节

        readByte(bufferHeap);
        readByte(bufferDirect);
        print("读取一个字节", bufferHeap);
        print("读取一个字节", bufferDirect);

在这里插入图片描述

3.1.5 标记回退

        bufferHeap.markReaderIndex();
        bufferDirect.markReaderIndex();

        readByte(bufferHeap);
        readByte(bufferDirect);
        print("读取一个字节", bufferHeap);
        print("读取一个字节", bufferDirect);

        System.out.println("回退");
        bufferHeap.resetReaderIndex();
        bufferDirect.resetReaderIndex();

        readByte(bufferHeap);
        readByte(bufferDirect);
        print("读取一个字节", bufferHeap);
        print("读取一个字节", bufferDirect);

在这里插入图片描述

3.1.6 slice

    // 无参 slice 是从原始 ByteBuf 的 read index 到 write index 之间的内容进行切片
        // slice 和 bufferHeap 共享一块内存
        ByteBuf slice = bufferHeap.slice();
        slice.setByte(0, 9);
        print("slice", slice);
        readByte(bufferHeap);

在这里插入图片描述

3.1.7 duplicate

        // 内存拷贝不共享内存
        ByteBuf duplicate = bufferHeap.duplicate();
        print("duplicate", duplicate);
        print("bufferHeap", bufferHeap);
        duplicate.writeBytes(new byte[]{5});
        print("duplicate", duplicate);
        print("bufferHeap", bufferHeap);

在这里插入图片描述

3.1.8 CompositeByteBuf

        // CompositeByteBuf 是一个组合的 ByteBuf,它内部维护了一个 Component 数组,
        // 每个 Component 管理一个 ByteBuf,记录了这个 ByteBuf 相对于整体偏移量等信息,代表着整体中某一段的数据。
        // 优点,对外是一个虚拟视图,组合这些 ByteBuf 不会产生内存复制
        // 缺点,复杂了很多,多次操作会带来性能的损耗
        ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer(5);
        buf1.writeBytes(new byte[]{1, 2, 3, 4, 5});
        ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer(5);
        buf2.writeBytes(new byte[]{6, 7, 8, 9, 10});
        CompositeByteBuf buf3 = ByteBufAllocator.DEFAULT.compositeBuffer();
        // true 表示增加新的 ByteBuf 自动递增 write index, 否则 write index 会始终为 0
        buf3.addComponents(true, buf1, buf2);
        print("buf3", buf3);

在这里插入图片描述

3.1.9 retain & release

3.1.9.1 retain & release

Netty 这里采用了引用计数法来控制回收内存,每个 ByteBuf 都实现了 ReferenceCounted 接口

  • 每个 ByteBuf 对象的初始计数为 1
  • 调用 release 方法计数减 1,如果计数为 0,ByteBuf 内存被回收
  • 调用 retain 方法计数加 1,表示调用者没用完之前,其它 handler 即使调用了 release 也不会造成回收
  • 当计数为 0 时,底层内存会被回收,这时即使 ByteBuf 对象还在,其各个方法均无法正常使用
        bufferHeap.retain();
        bufferDirect.retain();
        bufferHeap.release();
        bufferDirect.release();
        print("release bufferHeap", bufferHeap);
        print("release bufferDirect", bufferDirect);
        bufferHeap.release();
        bufferDirect.release();
        print("release bufferHeap", bufferHeap);
        print("release bufferDirect", bufferDirect);
        bufferHeap.release();
        bufferDirect.release();
        print("release bufferHeap", bufferHeap);
        print("release bufferDirect", bufferDirect);

在这里插入图片描述

3.1.9.2 Netty TailContext release

io.netty.channel.DefaultChannelPipeline.TailContext

io.netty.channel.DefaultChannelPipeline.TailContext#channelRead

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            onUnhandledInboundMessage(ctx, msg);
        }

io.netty.channel.DefaultChannelPipeline#onUnhandledInboundMessage(ChannelHandlerContext, Object)

    protected void onUnhandledInboundMessage(ChannelHandlerContext ctx, Object msg) {
        onUnhandledInboundMessage(msg);
        if (logger.isDebugEnabled()) {
            logger.debug("Discarded message pipeline : {}. Channel : {}.",
                         ctx.pipeline().names(), ctx.channel());
        }
    }

3.2 完整实例


import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;

public class ByteBufStudy {
    public static void main(String[] args) {
        // 堆内存的ByteBuf
        ByteBuf bufferHeap = ByteBufAllocator.DEFAULT.heapBuffer();
        // 直接内存的ByteBuf
        ByteBuf bufferDirect = ByteBufAllocator.DEFAULT.directBuffer();
        System.out.println(bufferHeap);
        System.out.println(bufferDirect);

        bufferHeap.writeBytes(new byte[]{1, 2, 3, 4});
        bufferDirect.writeBytes(new byte[]{1, 2, 3, 4});

        print("第一次写入", bufferHeap);
        print("第一次写入", bufferDirect);

        for (int i = 0; i < 100; i++) {
            bufferHeap.writeBytes(new byte[]{1, 2, 3, 4});
            bufferDirect.writeBytes(new byte[]{1, 2, 3, 4});
        }
        print("批量写入&扩容", bufferHeap);
        print("批量写入&扩容", bufferDirect);

        readByte(bufferHeap);
        readByte(bufferDirect);
        print("读取一个字节", bufferHeap);
        print("读取一个字节", bufferDirect);

        bufferHeap.markReaderIndex();
        bufferDirect.markReaderIndex();

        readByte(bufferHeap);
        readByte(bufferDirect);
        print("读取一个字节", bufferHeap);
        print("读取一个字节", bufferDirect);

        System.out.println("回退");
        bufferHeap.resetReaderIndex();
        bufferDirect.resetReaderIndex();

        readByte(bufferHeap);
        readByte(bufferDirect);
        print("读取一个字节", bufferHeap);
        print("读取一个字节", bufferDirect);

        // 无参 slice 是从原始 ByteBuf 的 read index 到 write index 之间的内容进行切片
        // slice 和 bufferHeap 共享一块内存
        ByteBuf slice = bufferHeap.slice();
        slice.setByte(0, 9);
        print("slice", slice);
        readByte(bufferHeap);

        // 内存拷贝不共享内存
        ByteBuf duplicate = bufferHeap.duplicate();
        print("duplicate", duplicate);
        print("bufferHeap", bufferHeap);
        duplicate.writeBytes(new byte[]{5});
        print("duplicate", duplicate);
        print("bufferHeap", bufferHeap);

        // CompositeByteBuf 是一个组合的 ByteBuf,它内部维护了一个 Component 数组,
        // 每个 Component 管理一个 ByteBuf,记录了这个 ByteBuf 相对于整体偏移量等信息,代表着整体中某一段的数据。
        // 优点,对外是一个虚拟视图,组合这些 ByteBuf 不会产生内存复制
        // 缺点,复杂了很多,多次操作会带来性能的损耗
        ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer(5);
        buf1.writeBytes(new byte[]{1, 2, 3, 4, 5});
        ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer(5);
        buf2.writeBytes(new byte[]{6, 7, 8, 9, 10});
        CompositeByteBuf buf3 = ByteBufAllocator.DEFAULT.compositeBuffer();
        // true 表示增加新的 ByteBuf 自动递增 write index, 否则 write index 会始终为 0
        buf3.addComponents(true, buf1, buf2);
        print("buf3", buf3);

        bufferHeap.retain();
        bufferDirect.retain();
        bufferHeap.release();
        bufferDirect.release();
        print("release bufferHeap", bufferHeap);
        print("release bufferDirect", bufferDirect);
        bufferHeap.release();
        bufferDirect.release();
        print("release bufferHeap", bufferHeap);
        print("release bufferDirect", bufferDirect);
        bufferHeap.release();
        bufferDirect.release();
        print("release bufferHeap", bufferHeap);
        print("release bufferDirect", bufferDirect);
    }

    public static void print(String prefix, ByteBuf buffer) {
        System.out.printf("%s readerIndex : %s writerIndex : %s maxCapacity : %s capacity : %s %n",
                prefix, buffer.readerIndex(), buffer.writerIndex(), buffer.maxCapacity(), buffer.capacity());
    }

    public static void readByte(ByteBuf buffer) {
        System.out.printf("读取一个字节: %s %n", buffer.readByte());}
}

4. 参考文献

  • 黑马 Netty教程
  • 拉钩教育 Netty 核心原理剖析与 RPC 实践 若地老师
    在这里插入图片描述

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

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

相关文章

网络通信架构

BS架构/CS架构 使用协议分别对应&#xff1a; TCP / HTTP 在计算机网络和软件开发中&#xff0c;CS架构&#xff08;Client-Server Architecture&#xff0c;客户端-服务器架构&#xff09;和BS架构&#xff08;Browser-Server Architecture&#xff0c;浏览器-服务器架构&am…

【5G核心网】5G NWDAF(Network Data Analytics Function)网元功能介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G技术研究。 博客内容主要围绕…

高级IO操作

高级I/O操作与非阻塞I/O 在操作系统中&#xff0c;I/O&#xff08;输入/输出&#xff09;操作是所有实现的基础。本文将探讨阻塞I/O与非阻塞I/O的区别&#xff0c;以及如何使用有限状态机来实现非阻塞I/O&#xff0c;并介绍数据中继的概念。 阻塞I/O与非阻塞I/O 阻塞I/O 阻…

Day7 —— 大数据技术之Hive

Hive快速入门系列 Hive的概述什么是Hive&#xff1f;使用Hive的原因 Hive架构Hive安装Hive配置文件修改启动Hive以命令行方式启动&#xff08;在$HIVE_HOME/bin目录下&#xff09;以JDBC连接启动&#xff08;beeline方式连接&#xff09; Hive基本操作Hive数据库操作Hive表操作…

小柴带你学AutoSar系列一、基础知识篇(6)车规级MCU入门RH850

flechazohttps://www.zhihu.com/people/jiu_sheng 小柴带你学AutoSar总目录https://blog.csdn.net/qiansh

已解决javax.swing.text.BadLocationException异常的正确解决方法,亲测有效!!!

已解决javax.swing.text.BadLocationException异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 出现问题的场景 报错原因 解决思路 解决方法 分析错误日志 检查索引合法性 捕获并处理异常 优化代码逻辑 确保线程安全操作…

使用AGG里面的clip_box函数裁剪画布, 绘制裁剪后的图形

// 矩形裁剪图片, 透明 void agg_testImageClipbox_rgba32(unsigned char* buffer, unsigned int width, unsigned int height) {// 创建渲染缓冲区 agg::rendering_buffer rbuf;// BMP是上下倒置的&#xff0c;为了和GDI习惯相同&#xff0c;最后一个参数是负值。rbuf.attach…

网吧管理系统带万字文档java项目基于springboot+vue的网吧管理系统java课程设计java毕业设计

文章目录 网吧管理系统一、项目演示二、项目介绍三、万字项目文档四、部分功能截图五、部分代码展示六、底部获取项目源码带万字文档&#xff08;9.9&#xffe5;带走&#xff09; 网吧管理系统 一、项目演示 网吧管理系统 二、项目介绍 基于springbootvue的网吧管理系统 系…

ENVI+SARscape操作:将tif DEM数据转换为SARscape可读写的格式

1、导入 tif DEM 数据 将下载的 tif DEM 数据&#xff08;ALOS DEM或SRTM DEM&#xff09;&#xff0c;直接将要导入的 tif DEM 数据拖入ENVI软件中&#xff0c;即可自动打开&#xff1b; 2、数据拼接 在 Toolbox 中&#xff0c;点击 Mosaicking->Seamless Mosaic&#xf…

任务调度SpringTask入门

任务调度简介 1.1什么是任务调度 在企业级应用中&#xff0c;经常会制定一些“计划任务”&#xff0c;即在某个时间点做某件事情&#xff0c;核心是以时间为关注点&#xff0c;即在一个特定的时间点&#xff0c;系统执行指定的一个操作。常见的任务调度框架有Quartz和SpringTa…

如何在Android中实现多线程与线程池?

目录 一、Android介绍二、什么是多线程三、什么是线程池四、如何在Android中实现多线程与线程池 一、Android介绍 Android是一种基于Linux内核的开源操作系统&#xff0c;由Google公司领导开发。它最初于2007年发布&#xff0c;旨在为移动设备提供一种统一、可扩展的操作系统。…

嵌入式实验---实验六 I2C传输实验

一、实验目的 1、掌握STM32F103I2C传输程序设计流程&#xff1b; 2、熟悉STM32固件库的基本使用。 二、实验原理 1、本案例利用I/O端口通过KEY01按键来控制STM32F103R6向24C02写入“hello”&#xff0c;通过另外一个按键KEY02来控制STM32F103R6从24C02读取“hello”&#x…

机器学习:线性回归模型学习路线

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

乳腺癌患者的生存分析:从传统方法到DeepSurv

一、引言 乳腺癌作为全球女性中最常见的癌症之一&#xff0c;其早期诊断和治疗对于提高患者生存率至关重要。随着医学技术和数据分析方法的不断发展&#xff0c;乳腺癌患者的生存分析逐渐成为研究的热点。通过生存分析&#xff0c;我们可以更好地理解疾病进展、预测患者预后&am…

【Oracle】实验二 体系结构、存储结构与各类参数

【实验目的】 理解Oracle体系结构了解初始化参数文件以及初始化参数的含义掌握查看三类数据字典视图和动态性能视图的方法 【实验内容】 如何查看初始化参数&#xff1f;有哪几种方法&#xff1f;初始化参数文件有几种&#xff1f;默认的保存位置在哪里&#xff1f;在SQL*Pl…

L57---112.路径总和(广搜)---Java版

1.题目描述 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。叶子节点 是指…

DVWA 靶场 Weak Session IDs 通关解析

前言 DVWA代表Damn Vulnerable Web Application&#xff0c;是一个用于学习和练习Web应用程序漏洞的开源漏洞应用程序。它被设计成一个易于安装和配置的漏洞应用程序&#xff0c;旨在帮助安全专业人员和爱好者了解和熟悉不同类型的Web应用程序漏洞。 DVWA提供了一系列的漏洞场…

https://curl.trillworks.com不能用的解决方法

gitee源码:https://gitee.com/Project0ne/curlconverter 首先打开上面的链接 然后下载文件 下载文件到本地 然后安装node.js(Node.js official website.)不会的自行百度,这里不做过多赘述。 在curlconverter文件夹下面打开终端(在文件夹下面右键-在终端打开) 输入 npm…

文本分析-中文停用词集合(结合百度停用词表、哈工大停用词表、四川大学机器智能实验室停用词库、中文停用词表等)

将这4个停用词表进行组合并去重。 # _*_coding:utf-8 _*_import os #得到当前文件路径 current_pathos.getcwd() #获取文件列表 list_fileos.listdir(current_path) #用来存储停用词 temp_stopwords[] for file in list_file:file_tailfile.split(.)[-1]#只要txt格式的文件if f…

Kylin系列:架构和高级功能详解

目录 一、Kylin的架构 1.1 总体架构概述 1.2 数据源 1.3 元数据存储 1.4 构建引擎 1.5 存储引擎 1.6 查询引擎 1.7 用户接口 二、Kylin的高级功能 2.1 多维立方体(Cube) 2.1.1 Cube的定义 2.1.2 Cube的构建 2.2 查询优化 2.3 数据模型和星型模式 2.3.1 数据模…