Netty 源码基础篇-- ByteBuf

netty 是一非常优秀的nio通信框架,今天就来聊聊ByteBuf的。此图不全。。

既然java 提供了nio 的有buffer 的netty 为什么又要自己进行封装的呢。

 主要是考虑了一下几点?java nio

      1》原生的api 太少,操作数据不是很方便,

      2〉原生的api 实例创建后,不能自动扩容,如果插入的数据超过剩余空间,会报异常。

 那看下Netty 的ByteBuf 都做了哪些改变。

     是通过 AbstractByteBuf 进行分析的。

     1、操作指针的改变,java nio 读取操作使用的一个指针,操作比较麻烦,易出错。netty引入了readerIndex 和 writerIndex

  +-------------------+------------------+------------------+
*      | discardable bytes |  readable bytes  |  writable bytes  |
*      |                   |     (CONTENT)    |                  |
*      +-------------------+------------------+------------------+
*      |                   |                  |                  |
*      0      <=      readerIndex   <=   writerIndex    <=    capacity

     2 看下readerIndex

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

    @Override
    public ByteBuf readerIndex(int readerIndex) {
        if (readerIndex < 0 || readerIndex > writerIndex) {
            throw new IndexOutOfBoundsException(String.format(
                    "readerIndex: %d (expected: 0 <= readerIndex <= writerIndex(%d))", readerIndex, writerIndex));
        }
        this.readerIndex = readerIndex;
        return this;
    }

    初始化时readerIndex 和writerIndex 都是0 。readerIndex提供了两个方式进行获取读取数据的位置,默认和指定。在指定的时候做了readerIndex 值不能为0 或者 大于writerIndex 的判断。注意返回值的不同。

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

    @Override
    public ByteBuf writerIndex(int writerIndex) {
        if (writerIndex < readerIndex || writerIndex > capacity()) {
            throw new IndexOutOfBoundsException(String.format(
                    "writerIndex: %d (expected: readerIndex(%d) <= writerIndex <= capacity(%d))",
                    writerIndex, readerIndex, capacity()));
        }
        this.writerIndex = writerIndex;
        return this;
    }

  这个同样是如此的。

 @Override
    public ByteBuf setIndex(int readerIndex, int writerIndex) {
        if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) {
            throw new IndexOutOfBoundsException(String.format(
                    "readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
                    readerIndex, writerIndex, capacity()));
        }
        this.readerIndex = readerIndex;
        this.writerIndex = writerIndex;
        return this;
    }

    @Override
    public ByteBuf clear() {
        readerIndex = writerIndex = 0;
        return this;
    }

这个 setIndex 和clear方法也很简单。

 @Override
    public boolean isReadable() {
        return writerIndex > readerIndex;
    }

    @Override
    public boolean isReadable(int numBytes) {
        return writerIndex - readerIndex >= numBytes;
    }

    @Override
    public boolean isWritable() {
        return capacity() > writerIndex;
    }

    @Override
    public boolean isWritable(int numBytes) {
        return capacity() - writerIndex >= numBytes;
    }

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

    @Override
    public int writableBytes() {
        return capacity() - writerIndex;
    }

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

这个是操作buffer的一些检测。也很简单。

  那看下buffer是如何控制写数据的,因为buffer 实现了7种基元类型的对写,用byte的来说。

 @Override
    public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
        ensureWritable(length);
        setBytes(writerIndex, src, srcIndex, length);
        writerIndex += length;
        return this;
    }

 @Override
    public ByteBuf writeChar(int value) {
        writeShort(value);
        return this;
    }

    @Override
    public ByteBuf writeFloat(float value) {
        writeInt(Float.floatToRawIntBits(value));
        return this;
    }

    @Override
    public ByteBuf writeDouble(double value) {
        writeLong(Double.doubleToRawLongBits(value));
        return this;
    }

在向buffer 写入时,首先进行了buffer剩余空间的检测。

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

        if (minWritableBytes <= writableBytes()) {
            return this;
        }

        if (minWritableBytes > maxCapacity - writerIndex) {
            throw new IndexOutOfBoundsException(String.format(
                    "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                    writerIndex, minWritableBytes, maxCapacity, this));
        }

        // Normalize the current capacity to the power of 2.
        int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);

        // Adjust to the new capacity.
        capacity(newCapacity);
        return this;
    }

首先进行了检测,如果小于0 ,直接抛异常,如果是在可写空间范围,直接返回,如果是大于最大容量,直接抛异常。

这个最大容量并不是一成不变的会根据方法

int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);

进行计算的。

 private int calculateNewCapacity(int minNewCapacity) {
        final int maxCapacity = this.maxCapacity;
        final int threshold = 1048576 * 4; // 4 MiB page

        if (minNewCapacity == threshold) {
            return threshold;
        }

        // If over threshold, do not double but just increase by threshold.
        if (minNewCapacity > threshold) {
            int newCapacity = minNewCapacity / threshold * threshold;
            if (newCapacity > maxCapacity - threshold) {
                newCapacity = maxCapacity;
            } else {
                newCapacity += threshold;
            }
            return newCapacity;
        }

        // Not over threshold. Double up to 4 MiB, starting from 64.
        int newCapacity = 64;
        while (newCapacity < minNewCapacity) {
            newCapacity <<= 1;
        }

        return Math.min(newCapacity, maxCapacity);
    }

使用了threshold 作为阈值进行计算,

如果插入的数据正好是4M,则直接返回,如果大于的话进行判断,进行一系列的判断,

 但是这个代码的意思是newCapacity的容量必须是2的倍数。

前面除是只能是2的倍数整除。

计算buf的容量,这里做了内存优化配置。首先判断写入的内存是否过大,使用4M(经验值)进行先判断,如果小于就使用后面的倍增。这里主要是防止内存的膨胀和浪费。

 

 

### Netty 源码分析文章汇总 对于希望深入了解Netty源码的读者来说,一系列深入浅出的文章能够提供极大的帮助。以下是按照同主题整理的一系列关于Netty源码解析的文章列表[^1]: #### 1. 初识Netty - **Netty入门**:介绍了Netty基础概念及其优势所在。 #### 2. 核心组件剖析 - **Netty架构设计**:探讨了Netty的整体框架设计理念。 - **Channel详解**:解释了`Channel`接口的作用以及其实现方式。 - **ChannelHandler介绍**:描述了如何利用`ChannelHandler`处理网络事件。 - **ChannelPipeline**:讲述了消息传递过程中所经过的管道机制。 #### 3. 数据传输优化 - **字节缓冲区 ByteBuf(上下)**:详细解读了高效内存管理工具`ByteBuf`的工作原理。 - **Netty如何实现零拷贝**:讨论了减少数据复制开销的技术细节。 #### 4. 应用层协议支持 - **编程引导类 Bootstrap**:指导用户快速搭建基于Netty的应用程序。 - **Reactor模型**:阐述了异步I/O多路复用技术在Netty中的应用。 - **编解码器系列** - **编码器/解码器基础** - **自定义编解码器** #### 5. 启动流程揭秘 - **Server端启动过程源码分析**:逐步拆解服务器实例初始化直至监听端口绑定完成的关键步骤[^5]。 这些资源仅适合初学者学习掌握基础知识,同时也非常适合有一定经验的研发人员进一步挖掘更深层次的功能特性。 ```java // 示例代码片段展示了NioServerSocketChannel的部分构造逻辑 public class NioServerSocketChannel extends AbstractNioMessageChannel { private final ServerSocketChannel javaChannel; public NioServerSocketChannel() throws ExceptionInInitializerError { this(ServerSocketChannel.open()); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值