Netty源码(一):Netty中的Buffer

最近我学习了NIO相关的知识,然后发现了Netty这个基于NIO的网络应用框架,于是就研究起Netty框架源码,来好好体会一下网络框架的设计理念和思想.
 这个系列的文章不仅会总结Netty各个模块的源码原理,也会写出一些自己对这些设计的理解和体会.
 我基本按照并发编程网上这个系列文章的顺序来进行系列文章的顺序,不同的是我是基于Netty4.1的源码进行分析和讲解.
 为了节约你的时间,本篇文章主要内容如下:

  • Netty的Buffer的内存模型,涉及读写指针
  • Netty的Buffer框架
  • Netty的Pool原理,轻量对象池

Buffer

Java NIO中的Buffer用于和NIO通道进行交互,数据可以从通道读入缓冲区,也可以从缓冲区写入到通道中.所以说,Buffer其实就是一块可以读写数据的内存,我们将其包装为一个Java对象来提供一系列读写操作.
 Netty并没有直接使用Java NIO的Buffer实现,而是自己实现了一套Buffer框架来满足自己的业务或者性能需求.

ByteBuf的基本原理

读写指针的作用

不同于NIO Buffer的读写指针共用原理,ByteBuf拥有readerIndex,writerIndex两个指针.下面我们就来详细的讲解一下ByteBuf的内部原理.

     +-------------------+------------------+------------------+
     | discardable bytes |  readable bytes  |  writable bytes  |
     |                   |     (CONTENT)    |                  |
     +-------------------+------------------+------------------+
     |                   |                  |                  |
     0      <=      readerIndex   <=   writerIndex    <=    capacity

从示意图中我们可以看出readerIndexwriterIndex最多可以将整个内容空间划分为三块:废弃区,可读区可写区.下面我们就来看一下不同操作下的两个指针的变化.

  • 在初始化状态下,假设capacity为20,readerIndexwriterIndex都为0,整个空间中只存在可写区.此时只能写,不能读,进行读操作会抛出异常.
       +---------------------------------------------------------+
       |             writable bytes (got more space)             |
       +---------------------------------------------------------+
       |                                                         |
 readerIndex(0)
  writerIndex(0)                   <=                   capacity
  • 写入10个字节的数据,writerIndex指向10,readerIndex不会改变,所有内容空间中有可读区和可写区.大小都是10字节.
       +-------------------+------------------+------------------+
       |  readable bytes  |  writable bytes                      |
       |     (CONTENT)    |                                      |
       +--------- --------+------------------+------------------
       |                  |                                      |
     readerIndex(0) <= writerIndex(10)           <=        capacity
  • 读取5个字节的内容,writerIndex不变,readerIndex加5,指向了5.此时内容空间分为了5字节的废弃区,5字节的可读区和10字节的可写区.

 +-------------------+------------------+------------------+
 | discardable bytes |  readable bytes  |  writable bytes  |
 |                   |     (CONTENT)    |                  |
 +-------------------+------------------+------------------+
 |                   |                  |                  |
 0      <=      readerIndex(5)   <=   writerIndex(10)    <=  capacity
  • 调用discardReadBytes方法后,将废弃区的内容舍弃掉,readerIndex又指向了0,writerIndex指向了5,相当于可读区和可写区整体向左平移了5个字节.
       +------------------+--------------------------------------+
       |  readable bytes  |    writable bytes (got more space)   |
       +------------------+--------------------------------------+
       |                  |                                      |
  readerIndex (0) <= writerIndex (5)              <=        capacity

零拷贝

OS层次上Zero-copy,就是在操作数据时,不需要将数据buffer从一个内存区域拷贝到另一个内存区域,因为减少了一次内存的拷贝,因此CPU的效率得到了提升.
Nettyzero-copy体现在很多方面.比如Buffer的compose,duplicate,slice操作时不会拷贝底层的数据.而是通过ByteBuf对象的组合来实现上述的操作

  • Netty提供了CompositeByteBuf类,可以将多个ByteBuf组合成一个逻辑上的Buffer,避免了各个buffer之间的拷贝,CompositeByteBuf并不拥有底层的数据,而是通过拥有两个buffer对象,从这两个buffer对象中获取数据来对外提供看似合并了的数据.比如我们将一份协议数据的头部buffer和消息体buffer合并成一个Buffer.

    1495838149-5833cca6c5c3d_articlex.png

     如上图所示,所有底层的数据还是存储在header和body这两个真实的buffer中.

  • 对于ByteBufsliceduplicate操作也是如此,不同的buffer共享了相同的底层数据,而不是进行底层数据的拷贝.具体使用到的Buffer类型为DuplicatedByteBufSlicedByteBuf.谁说是共享的底层数据,但是通过对writerIndexreaderIndex两个指针的操作来实现slice和duplicate的功能.

  • Netty使用wrap操作将byte数组转化为ByteBuf对象时,将byte数组包裹到对象中,而不是拷贝数组存放到对象中.

  • Netty 中使用 FileRegion 实现文件传输的零拷贝, 不过在底层 FileRegion 是依赖于 Java NIO FileChannel.transfer 的零拷贝功能.

Pool和Reference Count

4.0之后的版本实现了高性能的Buffer池,分配策略则是结合了buddy allocation和slab allocation的jemalloc变种,实现类为PoolArena,这样的话,可以在频繁分配和释放Buffer时缓解GC压力,还可以在初始化新buffer时减少内存带宽消耗(初始化时不可避免的要给buffer数组赋初始值).
ByteBuf引入了Reference Count机制,你需要在不适用它的时候调用ReferenceCountUtil.release方法来减少它的引用.

后记

 感觉自己在研究或在阅读源代码时还是有些问题,起始ByteBuf并不是Netty的关键所在,不应该花费这么长时间.以后还是要带着目的来看源码,不能把时间浪费在一些代码细节上.

引用

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

推荐阅读更多精彩内容