转载自:http://blog.csdn.net/iter_zc/article/details/39478111
Netty的ByteBuf缓冲区实现地比Java本身的ByteBuffer更加灵活,方便。可以参考:http://blog.csdn.net/zero__007/article/details/51326657
为什么要ByteBuf这个组件。主要还是因为基于select / poll / epoll这种IO多路复用技术的NIO是非阻塞同步IO的模型,由于是同步IO,需要用户线程自己来处理IO的读写,由于是非阻塞的,每次调用read, write读写的字节数是不确定的,所以非阻塞同步IO必须有缓冲区这个组件来保存每次读写的中间状态,通过缓冲区来确定是否读写完成。 ByteBuf不是对ByteBuffer的封装,而是重新实现了一个缓冲区。ByteBuffer只使用了一个position指针来记录当前的读写位置,ByteBuf使用了两个指针readerIndex, writerIndex分别来记录当前的读写位置,使用起来更加简单和方便。
ByteBuffer是一个固定长度的缓冲区,当put方法要写的数据大于可写的容量时会抛出异常。ByteBuf改进了这个设计,支持自动扩容。每次put之前会检查是否可以完全写入,如果不能,就会自动扩展ByteBuf的容量,保证put方法不会抛出异常。
和ByteBuffer一样,ByteBuf也支持堆内缓冲区和堆外直接缓冲区,根据经验来说,底层IO处理线程的缓冲区使用堆外直接缓冲区,减少一次IO复制。业务消息的编解码使用堆内缓冲区,分配效率更高,而且不涉及到内核缓冲区的复制问题。
从内存分配的角度看,ByteBuf可以分2类:
1)堆内存(HeapByteBuf)字节缓冲区:特点是内存的分配和回收速度快,可以被JVM自动回收;缺点就是如果进行Socket的I/O读写,需要额外做一次内存复制,将堆内存对应的缓冲区复制到内存Channel中,性能会有一定程度的下降;
2)直接内存(DirectByteBuf)字节缓冲区:非堆内存,它在堆外进行内存分配,相对于堆内存,它的分配和回收速度会慢一些,但是将它写入或者从Socket Channel中读取时,由于少了一次内存复制,速度比堆内存快。
最佳实践:在I/O通信线程的读写缓冲区使用DirectByteBuf,后端业务消息的编解码模块使用HeapByteBuf。
从内存回收角度看,ByteBuf分2类:基于对象池的ByteBuf和普通ByteBuf。两者的主要区别就是基于对象池的ByteBuf可以重用ByteBuf对象,它自己维护一个内存池,可以循环利用创建的ByteBuf,提高内存使用效率,降低由于高负载导致的频繁GC。测试表明使用内存池后的Netty在高负载、大并发的冲击下内存和GC更加平稳。
ByteBuf的堆内缓冲区又分为内存池缓冲区PooledByteBuf和普通内存缓冲区UnpooledHeapByteBuf。PooledByteBuf采用二叉树来实现一个内存池,集中管理内存的分配和释放,不用每次使用都新建一个缓冲区对象。UnpooledHeapByteBuf每次都会新建一个缓冲区对象。在高并发的情况下推荐使用PooledByteBuf,可以节约内存的分配。在性能能够保证的情况下,可以使用UnpooledHeapByteBuf,实现比较简单。
ByteBuf相关的辅助类
ByteBufHolder
ByteBufHolder是ByteBuf的容器,在Netty中非常有用。例如HTTP协议的请求消息和应答消息都可以携带消息体,这个消息体在Netty中就是ByteBuf对象。由于不同的协议,消息体可以包含不同的协议字段和功能。所以需要对ByteBuf进行包装和抽象,不同的子类可以有不同实现。
为了满足这些定制化的需求,Netty抽象出了ByteBufHolder对象,它包含了一个ByteBuf,另外还提供了一些其他实用的方法,使用者实现ByteBufHolder接口后可以按需求封装自己的实现。
CompositeByteBuf
CompositeByteBuf允许将多个ByteBuf的实例组装到一起,形成一个统一的视图,有点类似于数据库将多个表的字段组装到一起统一用视图展示。
CompositeByteBuf在一些场景下非常有用,例如某个协议POJO对象包含2部分:消息头和消息体,它们都是ByteBuf对象。当需要对消息进行编码的时候需要进行整合,如果使用JDK的默认能力,有以下2种方式:
1)将某个BygeBuffer复制到另一个ByteBuffer中,或者创建一个新的ByteBuffer,将2者复制到新建的ByteBuffer中;
2)通过List或数组等容器,将消息头和消息体放到容器中进行统一维护和处理
上面的做法非常别扭,实际上遇到的问题跟数据库中视图解决的问题一致——缓冲区有多个,但是需要统一展示和处理,必须有存放它们的统一容器。为了解决这个问题,Netty提供了CompositeByteBuf。
ByteBufAllocator
ByteBufAllocator是字节缓冲区分配器,按照Netty的缓冲区实现不同,共有2种不同的分配器:基于内存池的字节缓冲区分配器和普通的字节缓冲区分配器。
ByteBufUtil
ByteBufUtil是一个非常有用的工具类,提供了一些列静态方法用于操作ByteBuf对象。