iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Netty分布式ByteBuf使用的底层实现方式源码解析
  • 914
分享到

Netty分布式ByteBuf使用的底层实现方式源码解析

2024-04-02 19:04:59 914人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录概述AbstractByteBuf属性和构造方法首先看这个类的属性和构造方法我们看几个最简单的方法我们重点关注第二个校验方法ensureWritable(length)我们跟到扩

概述

熟悉NIO的小伙伴应该对jdk底层byteBuffer不会陌生, 也就是字节缓冲区, 主要用于对网络底层io进行读写, 当channel中有数据时, 将channel中的数据读取到字节缓冲区, 当要往对方写数据的时候, 将字节缓冲区的数据写到channel中

但是jdk的byteBuffer是使用起来有诸多不便, 比如只有一个标记位置的指针position, 在进行读写操作时要频繁的通过flip()方法进行指针位置的移动, 极易出错, 并且byteBuffer的内存一旦分配则不能改变, 不支持动态扩容, 当读写的内容大于缓冲区内存时, 则会发生索引越界异常

Netty的ByteBuf对jdk的byteBuffer做了重新的定义, 同样是字节缓冲区用于读取网络io中的数据, 但是使用起来大大简化, 并且支持了自动扩容, 不用担心读写数据大小超过初始分配的大小

byteBuf根据其分类的不同底层实现方式有所不同, 有直接基于jdk底层byteBuffer实现的, 也有基于字节数组的实现的, 对于byteBuf的分类, 在后面的小节将会讲到

byteBuf中维护了两个指针, 一是读指针, 二是写指针, 两个指针相互独立, 在读操作的时候, 只会移动读指针, 通过指针位置记录读取的字节数

同样在写操作时, 也只会移动写指针, 通过写指针的位置记录写的字节数

在每次读写操作的过程中都会对指针的位置进行校验, 读指针的位置不能超过写指针, 否则会抛出异常

同样, 写指针不能超过缓冲区分配的内存, 则将对缓冲区做扩容操作

具体指针操作, 入下图所示:

AbstractByteBuf属性和构造方法

在讲AbstractByteBuf之前, 我们首先先了解一下ByteBuf这个类, 这是所有ByteBuf的最顶层抽象, 里面定义了大量对ByteBuf操作的抽象方法供子类实现

AbstractByteBuf同样也缓冲区的抽象类, 定义了byteBuf的骨架操作, 比如参数校验, 自动扩容, 以及一些读写操作的指针移动, 但具体的实现, 不同的bytebuf实现起来是不同的, 这种情况则交给其子类实现

AbstractByteBuf继承了这个类, 并实现了其大部分的方法

首先看这个类的属性和构造方法

//读指针
int readerIndex;
//写指针
int writerIndex;
//保存读指针
private int markedReaderIndex;
//保存写指针
private int markedWriterIndex;
//最大分配容量
private int maxCapacity;
protected AbstractByteBuf(int maxCapacity) {
    if (maxCapacity < 0) {
        throw new IllegalArgumentException("maxCapacity: " + maxCapacity + " (expected: >= 0)");
    }
    this.maxCapacity = maxCapacity;
}

我们可以看到在属性中定义了读写指针的成员标量, 和读写指针位置的保存

在构造方法中可以传入可分配的最大内存, 然后赋值到成员变量中

我们看几个最简单的方法

@Override
public int maxCapacity() {
    return maxCapacity;
}
@Override
public int readerIndex() {
    return readerIndex;
}
@Override
public int writerIndex() {
    return writerIndex;
}

获取最大内存, 获取读写指针这些方法, 对所有的bytebuf都是通用的, 所以可以定义在AbstractByteBuf中

我们以一个writeBytes方法为例, 让同学们熟悉AbstractByteBuf中哪些部分自己实现, 哪些部分则交给了子类实现:

@Override
public ByteBuf writeBytes(ByteBuf src) {
    writeBytes(src, src.readableBytes());
    return this;
}

这个方法是将源的ByteBuf(参数)中的字节写入到自身ByteBuf中

首先这里调用了自身的writeBytes方法, 并传入参数ByteBuf本身, 以及Bytebuf的可读字节数, 我们跟到readbleBytes()方法中, 其实就是调用了自身的方法:

@Override
public int readableBytes() {
    return writerIndex - readerIndex;
}

我们看到, 这里可读字节数就是返回了写指针到读指针之间的长度

我们再继续跟到writeBytes(src, src.readableBytes())中:

@Override
public ByteBuf writeBytes(ByteBuf src, int length) {
    if (length > src.readableBytes()) {
        throw new IndexOutOfBoundsException(String.fORMat(
                "length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src));
    }
    writeBytes(src, src.readerIndex(), length);
    src.readerIndex(src.readerIndex() + length);
    return this;
}

这里同样调用了自身的方法首先会对参数进行验证, 就是写入自身的长度不能超过源ByteBuf的可读字节数

这里又调用了一个wirte方法, 参数传入源Bytebuf, 其可读字节数, 写入的长度, 这里写入的长度我们知道就是源ByteBuf的可读字节数

我们再跟到writeBytes(src, src.readerIndex(), length);

public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) {
    ensureAccessible();
    ensureWritable(length);
    setBytes(writerIndex, src, srcIndex, length);
    writerIndex += length;
    return this;
}

我们重点关注第二个校验方法ensureWritable(length)

public ByteBuf ensureWritable(int minWritableBytes) {
    if (minWritableBytes < 0) {
        throw new IllegalArgumentException(String.format(
                "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
    }
    ensureWritable0(minWritableBytes);
    return this;
}

然后我们再跟到ensureWritable0(minWritableBytes)方法中:

private void ensureWritable0(int minWritableBytes) {
    if (minWritableBytes <= writableBytes()) {
        return;
    }
    if (minWritableBytes > maxCapacity - writerIndex) {
        throw new IndexOutOfBoundsException(String.format(
                "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s", 
                writerIndex, minWritableBytes, maxCapacity, this));
    }
    //自动扩容
    int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
    capacity(newCapacity);
}

开始做了两个参数校验, 第一个表示当前ByteBuf写入的长度如果要小于可写字节数, 则返回

第二个可以换种方式去看minWritableBytes+ writerIndex> maxCapacity 也就是需要写入的长度+写指针必须要小于最大分配的内存, 否则报错, 注意这里最大分配内存不带表当前内存, 而是byteBuf所能分配的最大内存

如果需要写入的长度超过了可写字节数, 并且需要写入的长度+写指针不超过最大内存, 则就开始了ByteBuf非常经典也非常重要的操作, 也就是自动扩容

int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);

其中alloc()返回的是当前bytebuf返回的缓冲区分配器对象, 我们之后的小节会讲到, 这里调用了其calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity)方法为其扩容, 其中传入的参数writerIndex + minWritableBytes代表所需要的容量, maxCapacity为最大容量

我们跟到扩容的方法里面去

public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
    //合法性校验
    if (minNewCapacity < 0) {
        throw new IllegalArgumentException("minNewCapacity: " + minNewCapacity + " (expectd: 0+)");
    }
    if (minNewCapacity > maxCapacity) {
        throw new IllegalArgumentException(String.format(
                "minNewCapacity: %d (expected: not greater than maxCapacity(%d)", 
                minNewCapacity, maxCapacity));
    }
    //阈值为4mb
    final int threshold = 1048576 * 4;
    //最小需要扩容内存(总内存) == 阈值
    if (minNewCapacity == threshold) {
        //返回阈值
        return threshold;
    }
    //最小扩容内存>阈值
    if (minNewCapacity > threshold) {
        //newCapacity为需要扩容内存
        int newCapacity = minNewCapacity / threshold * threshold;
        //目标容量+阈值>最大容量
        if (newCapacity > maxCapacity - threshold) {
            //将最大容量作为新容量
            newCapacity = maxCapacity;
        } else {
            //否则, 目标容量+阈值
            newCapacity += threshold;
        }
        return newCapacity;
    }
    //如果小于阈值
    int newCapacity = 64;
    //目标容量<需要扩容的容量
    while (newCapacity < minNewCapacity) {
        //倍增
        newCapacity <<= 1;
    }
    //目标容量和最大容量返回一个最小的
    return Math.min(newCapacity, maxCapacity);
}

扩容相关的逻辑注释也写的非常清楚, 如果小于阈值(4mb), 采用倍增的方式, 如果大于阈值(4mb), 采用平移4mb的方式

我们回到writeBytes(ByteBuf src, int srcIndex, int length):

public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) {
    ensureAccessible();
    ensureWritable(length);
    setBytes(writerIndex, src, srcIndex, length);
    writerIndex += length;
    return this;
}

再往下看setBytes(writerIndex, src, srcIndex, length), 这里的参数的意思是从当前byteBuf的writerIndex节点开始写入, 将源缓冲区src的读指针位置, 写lenght个字节, 这里的方法中AbstractByteBuf类并没有提供实现, 因为不同类型的BtyeBuf实现的方式是不一样的, 所以这里交给了子类去实现

最后将写指针后移length个字节

最后我们回到writeBytes(ByteBuf src, int length)方法中:

public ByteBuf writeBytes(ByteBuf src, int length) {
    if (length > src.readableBytes()) {
        throw new IndexOutOfBoundsException(String.format(
                "length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src));
    }
    writeBytes(src, src.readerIndex(), length);
    src.readerIndex(src.readerIndex() + length);
    return this;
}

当writeBytes(src, src.readerIndex(), length)写完之后, 通过src.readerIndex(src.readerIndex() + length)将源缓冲区的读指针后移lenght个字节

以上对AbstractByteBuf的简单介绍和其中写操作的方法的简单剖析

以上就是Netty分布式ByteBuf使用的底层实现方式源码解析的详细内容,更多关于Netty分布式ByteBuf使用底层实现的资料请关注编程网其它相关文章!

--结束END--

本文标题: Netty分布式ByteBuf使用的底层实现方式源码解析

本文链接: https://www.lsjlt.com/news/144144.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Netty分布式ByteBuf使用的底层实现方式源码解析
    目录概述AbstractByteBuf属性和构造方法首先看这个类的属性和构造方法我们看几个最简单的方法我们重点关注第二个校验方法ensureWritable(length)我们跟到扩...
    99+
    2024-04-02
  • Netty分布式ByteBuf的分类方式源码解析
    目录ByteBuf根据不同的分类方式 会有不同的分类结果1.Pooled和Unpooled2.基于直接内存的ByteBuf和基于堆内存的ByteBuf3.safe和unsafe上一小...
    99+
    2024-04-02
  • Netty分布式ByteBuf缓冲区分配器源码解析
    目录缓冲区分配器以其中的分配ByteBuf的方法为例, 对其做简单的介绍跟到directBuffer()方法中我们回到缓冲区分配的方法然后通过validate方法进行参数验...
    99+
    2024-04-02
  • Netty分布式ByteBuf使用命中缓存的分配解析
    目录分析先关逻辑之前, 首先介绍缓存对象的数据结构我们以tiny类型为例跟到createSubPageCaches方法中回到PoolArena的allocate方法中我们跟到norm...
    99+
    2024-04-02
  • Netty分布式ByteBuf使用page级别的内存分配解析
    目录netty内存分配数据结构我们看PoolArena中有关chunkList的成员变量我们看PoolSubpage的属性我们回到PoolArena的allocate方法我们跟进al...
    99+
    2024-04-02
  • Netty分布式ByteBuf使用的回收逻辑剖析
    目录ByteBuf回收这里调用了release0, 跟进去我们首先分析free方法我们跟到cache中回到add方法中我们回到free方法中前文传送门:ByteBuf使用subPag...
    99+
    2024-04-02
  • Netty分布式ByteBuf使用directArena分配缓冲区过程解析
    目录directArena分配缓冲区回到newDirectBuffer中我们跟到newByteBuf方法中跟到reuse方法中跟到allocate方法中1.首先在缓存上进行分配2.如...
    99+
    2024-04-02
  • Netty分布式ByteBuf使用subPage级别内存分配的方法
    这篇文章主要介绍“Netty分布式ByteBuf使用subPage级别内存分配的方法”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Netty分布式ByteBuf使用subPage级别内存分配的方法”...
    99+
    2023-06-29
  • Netty分布式ByteBuf使用subPage级别内存分配剖析
    目录subPage级别内存分配我们其中是在构造方法中初始化的, 看构造方法中其初始化代码在构造方法中创建完毕之后, 会通过循环为其赋值这里通过normCapacity拿到tableI...
    99+
    2024-04-02
  • Netty分布式NioEventLoop优化selector源码解析
    目录优化selectorselector的创建过程代码剖析这里一步创建了这个优化后的数据结构最后返回优化后的selector优化selector selector的创建过程...
    99+
    2024-04-02
  • Netty分布式行解码器逻辑源码解析
    目录行解码器LineBasedFrameDecoder首先看其参数我们跟到重载的decode方法中我们看findEndOfLine(buffer)方法前文传送门:Netty分布式固定...
    99+
    2024-04-02
  • Netty分布式ByteBuf使用SocketChannel读取数据过程剖析
    目录Server读取数据的流程我们首先看NioEventLoop的processSelectedKey方法这里会走到DefaultChannelConfig的getAllocator...
    99+
    2024-04-02
  • Netty分布式ByteBuf使用的回收逻辑是什么
    这篇文章主要介绍“Netty分布式ByteBuf使用的回收逻辑是什么”,在日常操作中,相信很多人在Netty分布式ByteBuf使用的回收逻辑是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Netty分布...
    99+
    2023-06-29
  • redisson实现分布式锁的源码解析
    目录redisson测试代码加锁设计锁续期设计锁的自旋重试解锁设计撤销锁续期解锁成功唤排队线程 redisson redisson 实现分布式锁的机制如下: 依赖版本 implem...
    99+
    2024-04-02
  • Netty分布式ByteBuf如何使用page级别的内存分配
    这篇文章主要为大家展示了“Netty分布式ByteBuf如何使用page级别的内存分配”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Netty分布式ByteBuf如何使用page级别的内存分配”...
    99+
    2023-06-29
  • Netty分布式flush方法刷新buffer队列源码分析
    本文小编为大家详细介绍“Netty分布式flush方法刷新buffer队列源码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Netty分布式flush方法刷新buffer队列源码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入...
    99+
    2023-06-29
  • Netty分布式flush方法刷新buffer队列源码剖析
    目录flush方法这里最终会调用AbstractUnsafe的flush方法跟进addFlush方法回到addFlush方法回到AbstractUnsafe的flush方法我们重点关...
    99+
    2024-04-02
  • Netty分布式pipeline管道传播outBound事件源码解析
    目录outbound事件传输流程这里我们同样给出两种写法跟到其write方法中:跟到findContextOutbound中回到write方法:继续跟invokeWrite0我们跟到...
    99+
    2024-04-02
  • Netty分布式pipeline管道异常传播事件源码解析
    目录传播异常事件简单的异常处理的场景我们跟到invokeChannelRead这个方法我还是通过两种写法来进行剖析跟进invokeExceptionCaught方法跟到invokeE...
    99+
    2024-04-02
  • 如何实现PHP底层的分布式缓存
    如何实现PHP底层的分布式缓存随着互联网和大数据时代的到来,对于系统的性能和响应时间要求越来越高。分布式缓存作为提高系统性能的一种重要方式,被广泛应用于各种Web应用中。本文将介绍如何使用PHP底层来实现分布式缓存,并提供具体的代码示例。一...
    99+
    2023-11-08
    PHP分布式 缓存实现 底层技术
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作