Netty之ByteBuf

我们在进行数据传送的时候需要用到缓冲区,常用的就是NIO的Buffer,实际上7种类型(除了Boolean)都有相关的实现,但是NIO编程常用的还是ByteBuffer。但是ByteBuffer有position,limit,capacity位置,每次读写还需要flip和clear处理相对比较麻烦,所以Netty自己使用ByteBuf来替换NIO的缓冲。

1.ByteBuf的原理

ByteBuf通过两个指针来协助缓冲区的读写操作,读操作用readerIndex,写操作用writeIndex;

readerIndex和writerIndex的取值一开始都是0,随着数据的写入writerIndex会增加,读取数据会使readerIndex增加,但是它不会超过writerIndex.在读取之后,0~readerIndex的就被视为discard的,调用discardReadBytes方法,可以释放这部分空间,它的作用类似ByteBuffer的compact方法。 ReaderIndex和writerIndex 之间的数据是可读取的,等价于ByteBuffer position和limit之间的数据。WriterIndex 和capacity之间的空间是可写的,等价于ByteBuffer limit 和capacity之间的可用空间。

由于写操作不修改readerIndex指针,读操作不修改writerIndex指针,因此读写之间不再需要调整位置指针,这极大地简化了缓冲区的读写操作,避免了由于遗漏或者不熟悉flip()操作导致的功能异常。

具体操作流程如下图:

image

2.ByteBuf的功能

2.1 顺序读read操作

byteBuf的read操作ByteBuffer的get操作,有许多可以查看相关api。

比如readBytes()

2.2 顺序写write操作

byteBuf的write操作ByteBuffer的put操作,有许多可以查看相关api。

比如writeBytes()

2.3 readerIndex和writerIndex

Netty提供了两个指针变量用于支持顺序读取和写入操作:readerIndex用于标识读取索引,writerIndex 用于标识写入索引。两个位置指针将ByteBuf缓冲区分割成三个区域.

调用ByteBuf 的read操作时,从readerIndex处开始读取。readerIndex 到writerIndex之间的空间为可读的字节缓冲区;从writerIndex到cappacitcity 之间为可写的字节缓冲区; 0到readerIndex之间是已经读取过的缓冲区,可以调用discardReadBytes操作来重用这部分空间,以节约内存,防止ByteBuf的动态扩张。这在私有协议栈消息解码的时候非常有用,因为TCP底层可能粘包,几百个整包消息被TCP粘包后作为一个整包发送,这样,通过discardReadBytes操作可以重用之前已经解码过的缓冲区,这样就可以防止接收缓冲区因为容量不足导致的扩张。

2.4 Discardable bytes

清理掉已经读取到的缓冲区。

2.5 ReadableBytes 和WritableBytes

可以查看可读字节以及可写的字节数

2.6 Clear,SkipBytes操作

clear 会将readerIndex和writerIndex全部设置为0,内容不会清除

SkipBytes跳过不需要读取的字节或者字节数组。

2.7 Mark和Reset操作

如果某个时候我们需要对于某一个操作做回滚,那么mark和reset就是这样的实现的,他们只是改变索引位置,并不会 改变缓冲区内容。通过mark操作将当前指针备份到mark中,当调用reset操作恢复备份的mark值。

比如:

markReaderIndex
resetReaderIndex

markWriterIndex
resetWriterIndex

2.8 查找操作

我们可以通过某一个字符定位他的位置做到定位操作。

比如indexOf,bytesBefore,forEachByte等。

2.9 Derived buffers 新建视图

类似数据库视图,可以创建byteBuf的视图或者复制byteBuf

  • 1)duplicate:返回当前ByteBuf的复制对象,复制的只是指针,内容还是原来的内容,如果原来的内容变了,之后的byteBuf获得的内容也是变化后的。

  • 2)copy:完全复制新的ByteBuf,内容和索引都是完全互不影响

  • 3)slice:返回当前可读缓冲区即readerIndex到writerIndex,返回后的ByteBuf和原来的共享内容,但是指针各种维护

2.10 转换为标准的ByteBuffer

将byteBuf转换为对应的ByteBuffer

2.11 随机读写(set/get)

除了顺序的read和write外,也支持随机读写,不过不同的地方就是顺序读写可以做到自动扩容,随机读写会对索引和长度进行校验,不会动态扩展缓冲区,所以需要注意字节长度,否则会抛出越界的错误。

3.ByteBuf的辅助类

3.1 ByteBufHolder

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

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

3.2 ByteBufferAllocator

ByteBufAllocator是字节缓冲区分配器,按照Netty的缓冲区实现不同,共有两种不同的分配器:基于内存池的字节缓冲区分配器和普通的字节缓冲区分配器。

  • UnpooledByteBufAllocator

  • PooledByteBufAllocator

他们分别可以创建堆内缓冲和非堆内缓冲

3.3 CompositeByteBuf

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

3.4 ByteBufUtil

ByteBufUtil是一个非常有用的工具类,它提供了一系列静态方法用于操作ByteBuf对象.

比较有用的比如encodeString,decodeString,hexDump等方法。

4. 内存分配和回收

从内存分配的角度看,ByteBuf 可以分为两类:

(1)堆内存(HeapByteBuf) 字节缓冲区:特点是内存的分配和回收速度快,可以被 JVM自动回收;缺点就是如果进行Socket 的I/O 读写,需要额外做一次内存复制,将堆 内存对应的缓冲区复制到内核Channel中,性能会有一定程度的下降。

(2)直接内存( DirectByteBuf) 字节缓冲区:非堆内存,它在堆外进行内存分配,相 比于堆内存,它的分配和回收速度会慢--些,但是将它写入或者从SocketChannel中读取 时,由于少了一次内存复制,速度比堆内存快。

正是因为各有利弊,所以Netty提供了多种ByteBuf供开发者使用,经验表明,ByteBuf 的最佳实践是在I/O通信线程的读写缓冲区使用DirectByteBuf,后端业务消息的编解码模 块使用HeapByteBuf,这样组合可以达到性能最优。

从内存回收角度看,ByteBuf也分为两类:基于对象池的ByteBuf和普通ByteBuf。两 者的主要区别就是基于对象池的ByteBuf可以重用ByteBuf对象,它自己维护了一个内存 池,可以循环利用创建的ByteBuf,提升内存的使用效率,降低由于高负载导致的频繁GC。 测试表明使用内存池后的Netty在高负载、大并发的冲击下内存和GC更加平稳。 尽管推荐使用基于内存池的ByteBuf,但是内存池的管理和维护更加复杂,使用起来 也需要更加谨慎,因此,Netty提供了灵活的策略供使用者来做选择。

参考:《Netty权威指南》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容