• netty源码解析(4.0)-21 ByteBuf的设计原理


        io.netty.buffer包中是netty ByteBuf的实现。ByteBuf是一个二进制缓冲区的抽象接口,它的功能有:

    • 可以随机访问、顺序访问。
    • 支持基本数据类型(byte, short, int, long, float, double)的序列化和反序列化。
    • 不限制底层原始的数据类型,可以是byte[], NIO Buffer, String, IO Stream, 直接内(C语言中可以表示为指向一块内置起始地址的指针void*, 内存的长度), 等等。

    为什么需要ByteBuf

        缓冲区的使用范围非常广泛:I/O操作,序列化/反序列化,编码转换,压缩/解压,加/解密等所有需要使用byte[]的场景。
        有些场景需要需要能够快速地创建和销毁缓冲区,如:高并发的服务器,处理请求、返回响应的时时候需要大量且高频地创建,销毁缓冲区。
        有些场景不能确定需要多大的缓冲区,如: 从数据流中分离出一条消息,消息的长度不确定,只知道最大长度。假如消息的最大长度是64KB,而消息的平均长度只有4KB, 如果每次创建64KB的缓冲区就太浪费了。如果缓冲区能够在需要的时候自动且高效地增减容量,就完美了。
        在所有的场景中都涉及到频繁的数据copy,这要求缓冲区数据copy的性能要尽量高。如果有可能,尽量减少数据copy。
        ByteBuf就是为解决以上问题而设计的。

    核心概念

    如下所示

    |  discardable bytes | readable bytes |  writable bytes  |
    0                readerIndex       writerIndex       capacity
    
    • capacity: 缓冲区的容量。
    • readerIndex: 当前读的位置。可以使用readerIndex()和readerIndex(int)方法获取、设置readerIndex值。每次调用readXXX方法都会导致readerIndex向writerIndex移动,直到等于writerIndex为止。
    • writerIndex: 写的当前位置。可以使用writerIndex()和writerIndex(int)方法获取、设置writeIndex的值。每次调用writeXXX方法都会导致writeIndex向capacity移动,直到等于capacity为止。
    • discardable bytes: 可丢弃的数据。0--readerIndex之间的数据, 长度是readerIndex - 0,调用discardReadBytes会丢弃这部分数据,把readerIndex--writerIndex之间的数据移动到ByteBuf的开始位置(0), ByteBuf会变成如下所示的样子:
           |   readable bytes  |  writable bytes  |
    readerIndex(0)       writerIndex           capacity
    
    • readable bytes: 可读数据。 readerIndex--writerIndex之间的数据,长度是writerInex - readerIndex。可以调用readableBytes()方法得到它的长度。
    • writeable bytes: 可写的空间。长度是capacity - writerIndex。也可以认为它是ByteBuf的剩余空间。

    核心能力

    对二进制数据的读写是ByteBuf的核心能力。它提供两种读写方式:

    • 随机读写: getXXX(int), setXXX(int, ...)。不会对readerIndex和writerIndex产生影响,范围是(0,capacity]。
    • 顺序读写: readXXX, 增加readerIndex的值,范围是(readerIndex, writerIndex]。 writeXXX,增加writerIndex值,范围是(writerIndex, capacity]。

    ByteBuf为了方便使用,提供了一些基本数据类型(unsigned表示无符号类型)的读写支持:

    数据类型 长度(Byte)
    byte, unsignedByte 1
    short, unsignedShort 2
    char 2
    medium, unsignedMedium 3
    int, unsignedInt 4
    long 8
    float 4
    double 8

    对这些基本数据类型的读写涉及到了字节须的问题,ByteBuf支持两种字节序,使用java.nio.ByteOrder中的定义,默认的字节序是BIG_ENDIAN, 这个也是网络字节序。

    此外还提供了对byte[]类型及可以当成byte[]使用的数据类型的支持, 方法名都是:getBytes,setBytes, readBytes, writeBytes。

    • byte[]
    • ByteBuf
    • ByteBuffer
    • GatheringByteChannel
    • InputStread, OutputStream

    内存管理

    内存管理分为两个部分:内存分配,内存释放。
    ByteBufAllocator是内存分配的接口,它有两个具体实现:UnpooledByteBufAllocator, PooledByteBufAllocator。
    UnpooledByteBufAllocator是JVM内存分配接口的简单封装。
    PooledByteBufAllocator在JVM内存分配的基础上实现了一套独立的内存分配算法。
    内存释放的关键是如何判定对象死亡,ByteBuf继承了ReferenceCounted接口,使用引用计数的方式判定对象死亡。

    PooledByteBufAllocator中高效的内存管理算法是ByteBuf的性能基础,理解了它的算法是理解ByteBuf的关键。

    体系结构

    graph TD; B[ByteBuf]-->AB[AbstractByteBuf]; B-->SB[SwappedByteBuf]; B-->WB[WrappedByteBuf]; AB[AbstractByteBuf]-->ARCB[AbstractReferenceCountedByteBuf]; ARCB-->CB[CompositeByteBuf<br>FixedCompositeByteBuf]; ARCB-->PB[PooledByteBuf<T>]; ARCB-->UBB[UnpooledDirectByteBuf<br>UnpooledHeapByteBuf<br>UnpooledUnsafeDirectByteBuf<br>UnpooledUnsafeHeapByteBuf<br>UnpooledUnsafeNoCleanerDirectByteBuf]; ARCB-->ROBB[ReadOnlyByteBufferBuf]; PB[PooledByteBuf<T>]-->PDB[PooledDirectByteBuf]; PB-->PUDB[PooledUnsafeDirectByteBuf]; PB-->PHB[PooledHeapByteBuf]; PHB-->PUHB[PooledUnsafeHeapByteBuf];

    上图是ByteBuf体系结构中主要的类和接口。主要分为三大类:

    • AbstractReferenceCountedByteBuf及其子类。这个类别是是ByteBuf中最重要的部分,它分为4个小类别:

    CompositeByteBuf, FixedCompositeByteBuf: 把不同的ByteBuf组合成一个ByteBuf。
    PooledByteBuf:实现了自定义内存管理算法的。
    UnpooledXXXX: 直接使用JVM内存管理能力。
    ReadOnlyByteBufferBuf: 只读的。

    • SwappedByteBuf: 用来转换ByteBuf的字节序。
    • WrappedByteBuf: 用来包装另一个ByteBuf。

    工具

    有两个工具类帮助开发者使用ByteBuf:

    • ByteBufUtil: 这个类中创建好了默认的ByteBufAllocator,可以直接拿来用。还有一些操作ByteBuf常用的方法。
    • Unpooled: 这个类是针对UnpooledXXXByteBuf的工具。

    用法

    创建ByteBufAllocator

        使用ByteBufUtil.DEFAULT_ALLOCATOR得到ByteBufAllocator实例。这个实例可能是UnpooledByteBufAllocator,也可能是PooledByteBufAllocator类型,这取决于io.netty.allocator.type属性的设置。默认是unpooled,UnpooledByteBufAllocator类型。如果想要PooledByteBufAllocator类型,把这个属性的值设置成pooled:
        java -Dio.netty.allocator.type=pooled,或者System.setProperty("io.netty.allocator.type", "pooled")
        

    创建ByteBuf

        netty不建议直接创建创建ByteBuf实例,推荐使用ByteBufAllocator创建ByteBuf实例,或者使用Unpooled静态方法。
        ByteBufAllocator有7种方法用于创建ByteBuf实例:

    方法名 特性
    buffer 使用可能是JVM堆内存或直接内存,取决于具体的实现
    ioBuffer 如果可以的话优先使用直接内存
    heapBuffer 使用堆内存
    directBuffer 使用直接内存
    CompositeByteBuf 使用可能是JVM堆内存或直接内存,取决于具体的实现
    compositeHeapBuffer 使用堆内存
    compositeDirectBuffer 使用堆内存

    Unpooled创建ByteBuf实例的方法有2两种:

    方法名 特性
    buffer 使用堆内存
    directBuffer 使用直接内存

    包装成ByteBuf

    Unpooled提供了一系列的wrappedBuffer方法,把一些数据类型包装成一个ByteBuf, 这些数据类型有:

    • byte[]。
    • ByteBuffer。
    • 另一个的ByteBuf中的可读数据。

    wrappedBuffer方法还可以把byte[].., ByteBuffer.., ByteBuf..包装成一个CompositeByteBuf。

    数据读写

    数据读写是ByteBuf的基本功能,前面已经讲过,相关方法是: getXXX, readXXX, setXXX, writeXXX。

  • 相关阅读:
    SQL基础-子查询&EXISTS&UNION
    SQL基础-汇总统计及GROUP BY
    SQL基础-创建新的输出字段
    SQL基础-过滤数据
    获取图片为二进制流,并且显示图片到网页
    计算两个日期相差的工作小时数,过滤了周末双休小时数
    计算两个日期相差的工作天数,过滤了周末双休天数
    WEB性能测试工具
    C#整合VS2010和NUnit
    如何打开Windows Server 2008 R2的域安全策略
  • 原文地址:https://www.cnblogs.com/brandonli/p/11534491.html
Copyright © 2020-2023  润新知