您的位置:首页 > 其它

netty(十二)源码分析之ByteBuf 四

2017-08-28 16:41 821 查看
PooledDirectByteBuf基于内存池实现,与UnPooledDirectByteBuf的唯一不同就是缓冲区的分配与销毁策略不同,其他功能都是等同的,也就是说,两者唯一的不同就是内存分配策略不同。

1.创建字节缓冲区实例
由于采用内存池实现,所以新创建PooledDerectByteBuf对象时不能直接new一个实例,而是从内存池中获取,然后设置引用计数器的值,

static PooledDirectByteBuf newInstance(int maxCapacity) {
PooledDirectByteBuf buf = RECYCLER.get();
buf.reuse(maxCapacity);
return buf;
}

final void reuse(int maxCapacity) {
maxCapacity(maxCapacity);
setRefCnt(1);
setIndex0(0, 0);
discardMarks();
}


直接从内存池Recycler<PooledDirectByteBuf>中获取PooledDirectByteBuf对象,然后设置它的引用计数器为1,设置缓冲区最大容量后返回。设置readerIndex,writerIndex与相应mark都为0.

ByteBuf相关的辅助类功能介绍

ByteBufHolder

ByteBufHolder是ByteBuf的容器,在Netty中,它非常有用。例如HTTP协议的请求消息和应答消息都可以携带消息体,这个消息体在NIO ByteBuffer中就是个ByteBuffer对象,在Netty中就是ByteBuf对象。由于不同的协议消息体可以包含不同的协议字段和功能,因此,需要对ByteBuf进行包装和抽象,不同的子类可以有不同的实现。

为了满足这些定制化的需求,Netty抽象出了ByteBufHolder对象,它包含了一个ByteBuf,另外还提供了一些其他实用的方法,使用者集成ByteBufHolder接口后可以按需封装自己的实现。相关类库的继承关系如下图所示:


ByteBufAllocator

ByteBufAllocator是字节缓冲区分配器,按照Netty的缓冲区实现不同,共有两种不同的分配器:基于内存池的字节缓冲区分配器和普通的字节缓冲区分配器。接口的继承关系如下图所示:



下面我们给出ByteBufAllocator的主要API功能列表




CompositeByteBuf

CompositeByteBuf允许将多个ByteBuf的实例组装到一起,形成一个统一的视图,有点类似于数据库将多个表的字段组装到一起统一用视图展示。

CompositeByteBuf在一些场景下非常有用,例如某个协议POJO对象包含两部分:消息头和消息体,它们都是ByteBuf对象。当需要对消息进行编码的时候需要进行整合,如果使用JDK的默认能力,有以下两种方式。

(1)将某个ByteBuffr复制到另一个ByteBuffer中,或者创建一个新的ByteBuffer,将两者复制到新建的ByteBuffer中;

(2)通过List或数组等容器,将消息头和消息体放到容器中进行统一维护和处理。

上面的做法非常别扭,实际上我们遇到的问题跟数据库中视图解决的问题一致————缓冲区有多个,但是需要统一展示和处理,必须有存放它们的统一容器。为了解决这个问题,Netty提供了CompositeByteBuf。

其实现如下:

public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements Iterable<ByteBuf> {

private static final ByteBuffer EMPTY_NIO_BUFFER = Unpooled.EMPTY_BUFFER.nioBuffer();
private static final Iterator<ByteBuf> EMPTY_ITERATOR = Collections.<ByteBuf>emptyList().iterator();

private final ByteBufAllocator alloc;
private final boolean direct;
private final List<Component> components;
private final int maxNumComponents;

private boolean freed;它定义了一个Component类型的集合,实际上Component就是ByteBuf的包装实现类,它聚合了ByteBuf对象,维护了在集合中的位置偏移量信息等,它的实现如下:
private static final class Component {
final ByteBuf buf;
final int length;
int offset;
int endOffset;

Component(ByteBuf buf) {
this.buf = buf;
length = buf.readableBytes();
}

void freeIfNecessary() {
buf.release(); // We should not get a NPE here. If so, it must be a bug.
}
}

向CompositeByteBuf中新增一个ByteBuf的代码,如下所示:
public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer) {
checkNotNull(buffer, "buffer");
addComponent0(increaseWriterIndex, components.size(), buffer);
consolidateIfNeeded();
return this;
}

删除增加的ByteBuf源码,如下所示:
public CompositeByteBuf removeComponent(int cIndex) {
checkComponentIndex(cIndex);
Component comp = components.remove(cIndex);
comp.freeIfNecessary();
if (comp.length > 0) {
// Only need to call updateComponentOffsets if the length was > 0
updateComponentOffsets(cIndex);
}
return this;
}

ByteBufUtil是一个非常有用的工具类,它提供了一系列静态方法用于操作ByteBuf对象。它的功能列表如下:
(1)encodeString(ByteBufAllocator alloc,CharBuffer src,Charset charset):对需要编码的字符串src按照指定的字符集charset进行编码,利用指定的ByteBufAllocator生成一个新的ByteBuf;

(2)decodeString(ByteBuffer src,Charset charset):使用指定的ByteBuffer和charset进行对ByteBuffer进行编码,获取编码后的字符串。

还有一个非常有用的方法就是hexDump,它能够将参数ByteBuf的内容以十六进制字符串的方式打印出来,用于输出日志或者打印码流,方便问题定位,提升系统的可维护性。

总结:

我们介绍了与ByteBuf密切相关的工具类和辅助类。ByteBuf是Netty架构中最重要,最基础的数据结构,熟练地掌握和使用它是学好Netty的基本要求,也是成长为高级Netty开发人员的必经之路。后面我们一起研究Netty的另两个重要类库:Channel和Unsafe。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息