5.4 Interface ByteBufHolder
我们经常在ByteBuf中存储一些正常数据之外,我们有时候还需要增加一些各式各样的属性值,一个Http响应体就是一个很好的例子,除了按照字节传输过来的主体内容,还有状态码,cookie等信息
Netty提供了ByteBufHolder来处理这些常用的用户案例,ByteBufHolder还提供了Netty一些其他的先进特性,例如缓存池,缓存池可以是ByteBuf中直接“借出”获取,如果有需要,“借出”的ByteBuf还可以自动的还到池中
ByteBufHolder提供了一系列的获取底层数据和引用计数的方法,表5.6向你展示了一些常用的方法
如果你想要实现一个消息对象可以在ByteBuf中存储其有效负荷的话,使用ByteBufHolder是一个不错的选择
5.5 ByteBuf allocation
在这个小节中,我们将讨论管理ByteBuf实例的几种方法
5.5.1 On-demand: interface ByteBufAllocator
为了减少分配内存和释放内存的消耗,Netty用接口ByteBufAllocator来实现了管理池,这个池子可以分配我们定义的任何ByteBuf的实例,管理池的使用是应用程序的具体需求,它不会改变ByteBuf的默认的一些API
表5.7向你展示了ByteBufAllocator提供的一些操作
你可以从一个Channel或者从ChannelHandlerContext中获取一个ByteBufAllocator的引用,下面的代码清单说明了这些方法
Netty提供了ByteBufAllocator接口的两种具体的实现,PooledByteBufAllocator和UnpooledByteBufAllocator,前者池化存储ByteBuf来提高性能和减少内存碎片的出现,这种方法使用比较出名的高效的“jemalloc”来分配管理内存,这种技术已经被用在了目前比较流行的操作系统中了,或者没有池化存储ByteBuf的实例,而是每次调用返回一个新的实例
Netty默认是使用PooledByteBufAllocator,但是这可以通过修改ChannelConfig或者通过你在项目中bootstrap时指定特定的不同类型来轻易改变这个默认值,详细的信息可以参考第八章的内容
5.5.2 Unpooled buffers
在某些情况下,你可能没有ByteBufAllocator的引用,在这种情况下,Netty提供了一个特殊的类叫Unppoled,它提供了一个静态的工具方法来创造一个非池化的ByteBuf实例,表5.8展示了Unpooled中比较重要的方法
Unpooled的类也可以使ByteBuf不用于非网络的项目中,使这些项目可以在高性能的可扩展的bufferAPI下获益,并且使用这个不会牵扯到Netty的一些其他的组件
5.5.3 Class ByteBufUtil
ByteBufUtil提供了很多静态的工具方法来管控ByteBuf,因为ByteBufUtil的API是通用的,与池无关的,这些方法可以被除了分配内存的类实现之外,还可以供其他的类使用
在这些静态方法中最最重要的方法可能就是hexdump方法了,这个方法将ByteBuf包含的内容打印成一个十六进制的字符,这在很多情形下显得很是重要,当定位问题和输出日记的时候,十六进制的表示比直接获取byte的值更加友好,还有就是,十六进制版本的数据可以很轻易地转回真实的数据格式
另一个有用的方法就是equals的方法了,这个方法可以确认两个ByteBuf的实例是否相等,如果你实现了你自己的ByteBuf的类的话,你可能会发现ByteBufUtil其他的一些比较有用的方法
5.6 Reference counting
引用计数是一门优化内存使用的技术,如果一个对象所持有的资源不再被任何对象引用的时候,将会释放这部分的资源,Netty在其第四版本为ByteBuf和ByteBufHolder引入了引用计数这项技术,ByteBuf和ByteBufHolder都实现了ReferenceCounted技术
关于引用计数的技术的理论并不是很复杂,大部分情况下它会追踪一个具体对象被引用的个数,一个实现ReferenceCounted接口的实例一般对一个存活的对象开始计数为1,只要对该对象的引用的个数不是0的情况下,这个对象所持有的资源肯定不会被释放,当一个存活的对象引用的个数降至0,那么这个实例将会被释放,注意,关于"释放"的明确定义其实并不是很明确,但是至少如果一个对象已经被"释放",那么这个对象将不再能够被使用
引用计数对于池技术的实现是至关重要的,例如PooledByteBufAllocator,这个可以减少内存分配的损耗,在下面的两个代码清单中展示了这个例子
如果尝试获取一个引用计数的具体对象,但是该对象已经被释放的时候,会抛出一个IllegalReferenceCountException的异常
注意一个对象可以在它自己特有的方法中定义它自己的释放计数的契约,我们可以假设一下它的实现方法release只是设置它的引用计数为0,不管它真实的当前引用计数是多少的时候,这将使这个存活的对象立马无效变得不合法
WHO IS RESPONSIBLE FOR RELEASE?大体上讲,最后一次获取到该对象的负责释放它,在第六章中,我们将讲解它与它相关联的ChannelHandler和ChannelPipeline
5.7 Summary
这个章节致力于学习Netty的数据容器,容器都是基于ByteBuf的,我们一开始解释了ByteBuf的性能为什么比JDK的高,我们也强调了说明了一个个新的API,并解释了它们使用的每一个场景
以下,是我们刚刚提及过的:
1)read和write两个不同的索引控制这我们对数据的操作
2)不同的内存使用方法---backing arrays 和 direct buffers
3)可以使用CompositeByteBuf来使多种ByteBuf聚集
4)数据接入的方法:searching,slicing和copying
5)read,write,get和set这些方法的API
6)ByteBufAllocator池和引用计数
在接下来的一个章节中,我们关注的组件是ChannelHandler,它是我们数据处理逻辑的核心关键,因为ChannelHandler大类使用ByteBuf,你将开始看到Netty的一些核心组件将被整合在一起被使用