Netty学习笔记01-ByteBuf

netty权威指南学习笔记
netty实战

java.nio.Buffer

javaNIO对于7种基本数据类型(Boolean除外)都有自己的缓冲区实现,对于NIO而言我们最常使用的是ByteBuffer
这里写图片描述
从功能上看,ByteBuffer完全可以满足NIO编程的需要,但是ByteBuffer也有它自身的局限性:

  • ByteBuffer长度固定,一单分配完成,它的容量无法自动扩展和收缩
  • ByteBuffer只有一个位置表示position,读写操作时需要手工调用flip()和rewind()方法等
  public final Buffer flip() {
        limit = position; //限制设置为当前位置
        position = 0;//,然后将位置设置为 0
        mark = -1; //丢弃标记。
        return this;
    }

  public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }
  • ByteBuffer的API功能有限
    为了弥补这些不足,Netty提供了自己的ByteBuffer实现ByteBuf

ByteBuf工作说明

ByteBuf API 的优点:

  • 它可以被用户自定义的缓冲区类型扩展;
  • 通过内置的复合缓冲区类型实现了透明的零拷贝;
  • 容量可以按需增长(类似于 JDK 的StringBuilder);
  • 在读和写这两种模式之间切换不需要调用 ByteBuffer 的 flip()方法;
  • 读和写使用了不同的索引;
  • 支持引用计数;
  • 支持池化;

ByteBuf的工作原理
ByteBuf依然是一个Byte数组缓冲区,它的基本功能与nio的ByteBuffer一致,二者最大的不同有如下。
ByteBuffer只有一个指针用于读写操作,每次读写都需要调用flip(),clear()等方法,否则功能将会出错,
这里写图片描述
,而ByteBuf通过两个指针来协助缓冲区读写操作,读操作readerIndex,写操作用writerIndex.二者一开始都是0,随着数据的写入,writerIndex会增加,读取数据readerIndex增加,但是它不会超过writerIndex.
这里写图片描述

ByteBuf的功能介绍

  • 顺序读操作(read):类似于ByteBuffer的get操作,readXXX主要方法:
//读取一个32位的整数,readerIndex增加4
 public abstract int   readInt();

//读取一个64位的整数,readerIndex增加8
public abstract long  readLong();

//读取一个2字节UTF-16 character 
public abstract char  readChar();

/*
将当前ByteBuf中的数据读取到新的ByteBuf中去,读取长度为length;
返回的ByteBuf的readerIndex=0,writerIndex=length
*/
public abstract ByteBuf readBytes(int length);

//返回当前ByteBuf创建的子区域,子区域与原ByteBuf共享缓冲区,但是独立维护readerIndex和writerIndex
public abstract ByteBuf readSlice(int length);

/*
将当前ByteBuf的数据读取到目标ByteBuf,直到目标ByteBuf没有剩余空间可写。
若目标ByteBuf的可写空间 > 当前ByteBuf的可读空间,则抛出IndexOutOfBoundsExeption
*/
public abstract ByteBuf readBytes(ByteBuffer dst);
  • 顺序写操作(write):类似于ByteBuffer的put,writeXXX方法(略)
  • Mark和Rest:ByteBuf有读索引和写索引所以,它共有2*2个方法
    • markReaderIndex
    • markWriterIndex
    • resetReaderIndex
    • resetWriterIndex
  • 查找操作:从ByteBuf查找某个字符
 //从fromIndex到toIndex区间内,第一次出现value的位置索引
 public abstract int indexOf(int fromIndex, int toIndex, byte value);

 //从readerIndex到writerIndex区间内,第一次出现value的位置索引
 public abstract int bytesBefore(byte value);

 //从readerIndex到readerIndex+length区间内,第一次出现value的位置索引
 public abstract int bytesBefore(int length, byte value);

 /*
 与ByteBufProcessor 设置的条件匹配,第一次匹配成功的位置索引,
 ByteBufProcessor**接口**对常用的查询条件进行了抽象:
 FIND_CR:CR('\r') , FIND_LN:LN('\n')
 */
 public abstract int forEachByteDesc(ByteBufProcessor processor);
  • Derived Buffers(提取)
    • duplicate
    • copy
    • copy(int index,int length)
    • slice
  • 转换成标准NIO的ByteBuffer
    • nioBuffer()
    • nioBuffer(int index, int length)
  • 随机读写(get,set)

ByteBuf源码分析

ByteBuf的主要类图:
这里写图片描述

从内存分配角度看
- 堆内存(HeapByteBuf)字节缓冲区,特点是
- 内存分配和回收速度快,可以被JVM自动回收
- 缺点是进行Socket I/O时,需要额外做一次内存复制(将堆内存复制到内核的Channel中去)
- 直接内存(DirectByteBuf)字节缓冲区:非堆内存,在堆外进行内存分配(计算机内存),它的特点是:分配和回收速度慢,但是Socket I/O少了一次内存复制,比堆内存快。

经验表明,**使用ByteBuf的最佳实践:**I/O通信线程的读写缓冲区使用DirectByteBuf,后端业务消息的模块使用HeapByteBuf。

从内存回收角度看

  • 于对象池的ByteBuf(PoolxxxByteBuf):可以循环利用,降低高负载时导致的频繁GC,但是内存池的管理和维护复杂。
  • 普通ByteBuf(UnpoolxxxByteBuf)

AbstractByteBuf

      static final ResourceLeakDetector<ByteBuf> leakDetector = new ResourceLeakDetector<ByteBuf>(ByteBuf.class);

    int readerIndex; //读索引
    private int writerIndex;//写索引
    private int markedReaderIndex; //mark-reader
    private int markedWriterIndex;//mark-writer

    private int maxCapacity;//最大容量

    private SwappedByteBuf swappedBuf;

重点关注leakDetector,它被定义为static,意味着所有的ByteBuf共享一个ResourceLeakDetector<ByteBuf>,ResourceLeakDetector用于检测对象是否泄漏。

AbstractReferenceCountedByteBuf
从类的名称看该类主要对引用进行计数,类似于JVM内存回收的对象引用计数器,用于跟踪对象的分配和销毁,做自动内存回收。

   private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
            AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");

    private static final long REFCNT_FIELD_OFFSET;

    static {
        long refCntFieldOffset = -1;
        try {
            if (PlatformDependent.hasUnsafe()) {
                refCntFieldOffset = PlatformDependent.objectFieldOffset(
                        AbstractReferenceCountedByteBuf.class.getDeclaredField("refCnt"));
            }
        } catch (Throwable t) {
            // Ignored
        }

        REFCNT_FIELD_OFFSET = refCntFieldOffset;
    }

  @SuppressWarnings("FieldMayBeFinal")
    private volatile int refCnt = 1;

首先看refCntUpdater ,他是一个AtomicIntegerFieldUpdater类型的原子变量;
REFCNT_FIELD_OFFSET,它用来标识refCnt字段在AbstractReferenceCountedByteBuf的内存地址;
最后定义一个refCnt,用于跟踪对象的引用次数。


ByteBuf相关辅助类

  • ByteBufHolder:它是一个ByteBuf容器,针对不同的协议,需要对ByteBuf进行封装和抽象,为此Netty抽象出了ByteBufHolder
    这里写图片描述
  • ByteBufAllocator:字节缓冲区分配器,
    • PoolByteBufAllocator:基于内存池的字节缓冲分配器
    • UnpoolByteBufAllocator:普通的字节缓冲分配器
        Channel channel = null;
        ByteBufAllocator allocator = channel.alloc();

        ChannelHandlerContext ctx = null;
        ByteBufAllocator allocator2 = ctx.alloc();
  • CompositeByteBuf:复合缓冲区
    这里写图片描述
  • Unpooled:
    byte[] req = "QUERY TIME ORDER".getBytes();
    this.firstMsg = Unpooled.copiedBuffer(req);
  • ByteBufUtil它是一个非常又有的工具类,提供了一系列的静态方法。
 static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) ;
 static String decodeString(ByteBuffer src, Charset charset) ;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值