Netty源码剖析之核心组件

简介: NioEventLoop有以下核心功能。- 开启Selector并初始化。- 把ServerSocketChannel注册到Selector上。- 处理各种I/O事件,如OP_ACCEPT、OP_CONNECT、OP_READ、OP_WRITE事件。- 执行定时调度任务。- 解决JDK空轮询bug。

1、NIOEventLoop

NioEventLoop有以下有个核心功能。

  • 开启Selector并初始化。
  • 把ServerSocketChannel注册到Selector上。
  • 处理各种I/O事件,如OP_ACCEPT、OP_CONNECT、OP_READ、

OP_WRITE事件。

  • 执行定时调度任务。
  • 解决JDK空轮询bug。

NioEventLoop这些功能的具体实现大部分都是委托其他类来完成 的,其本身只完成数据流的接入工作

在这里插入图片描述

2、AbstractChannel

AbstractChannel抽象类包含以下几个重要属性。

  • EventLoop:每个Channel对应一条EventLoop线程。
  • DefaultChannelPipeline:一个Handler的容器,也可以将其理解为一个Handler链。Handler主要处理数据的编/解码和业务逻辑。
  • Unsafe:实现具体的连接与读/写数据,如网络的读/写、链路 关闭、发起连接等。命名为Unsafe表示不对外提供使用,并非不安全。

在这里插入图片描述

在这里插入图片描述

3、ByteBuf

网络传输中,字节是基本单位NIO使用ByteBuffer作为Byte字 节容器,但是其使用过于复杂。因此Netty写了一套Channel,代替了NIO 的 Channel 。 Netty 缓 冲 区 又 采 用 了 一 套 ByteBuf 代 替 了 NIO 的ByteBufferNetty的ByteBuf子类非常多,这里只对核心的ByteBuf进 行详细的剖析

在这里插入图片描述

NIO ByteBuffer只有一个位置指针position,在切换读/写状态 时,需要手动调用flip()方法或rewind()方法,以改变position的 值,而且ByteBuffer的长度是固定的,一旦分配完成就不能再进行扩 容和收缩,当需要放入或存储的对象大于ByteBuffer的容量时会发生异常。每次编码时都要进行可写空间校验。

Netty的AbstractByteBuf将读/写指针分离,同时在写操作时进行 了自动扩容。对其使用而言,无须关心底层实现,且操作简便、代码无冗余

3.1、AbstractByteBuf

AbstractByteBuf是ByteBuf的子类,它定义了一些公共属性,如 读索引、写索引、mark、最大容量等。AbstractByteBuf实现了一套 读/写操作的模板方法,其缓冲区真正的数据读/写由其子类完成

在这里插入图片描述

3.2、AbstractReferenceCountedByteBuf

Netty在进行I/O的读/写时使用了堆外直接内存,实现了零拷贝, 堆外直接内存Direct Buffer的分配与回收效率要远远低于JVM堆内存 上对象的创建与回收速率。Netty使用引用计数法来管理Buffer的引用 与释放。

Netty采用了内存池设计,先分配一块大内存,然后不断地重 复利用这块内存。例如,当从SocketChannel中读取数据时,先在大内 存块中切一小部分来使用,由于与大内存共享缓存区,所以需要增加 大内存的引用值,当用完小内存后,再将其放回大内存块中,同时减少其引用值

由于ByteBuf的操作可能存在多线程并发使用的情况,其refCnt属 性的操作必须是线程安全的,因此采用了volatile来修饰,以保证其 多线程可见。在Netty中,ByteBuf会被大量地创建,为了节省内存开 销,通过AtomicIntegerFieldUpdater来更新refCnt的值

而**没有采用AtomicInteger类型。因为AtomicInteger类型创建的对象比int类型多 占用16B的对象头,当有几十万或几百万ByteBuf对象时,节约的内存
可能就是几十MB或几百MB。**
在这里插入图片描述

3.3、ReferenceCountUpdater

ReferenceCountUpdater 是 AbstractReferenceCountedByteBuf 的 辅助类,用于完成对引用计数值的具体操作

在这里插入图片描述

3.4、CompositeByteBuf

CompositeByteBuf的主要功能是组合多个ByteBuf,对外提供统一 readerIndex和writerIndex。由于它只是将多个ByteBuf的实例组装 到一起形成了一个统一的视图,并没有对ByteBuf中的数据进行拷贝,因此也属于Netty零拷贝的一种,主要应用于编码和解码,注意:组合并不一定真的要将数据全部放入一个集合,我可以仅仅保留指向单个的引用指针

应用场景

编码时,将消息头和消息体两个ByteBuf组合到一块进行编码,可能 会觉得Netty有写缓冲区,其本身就会存储多个ByteBuf,此时只需把 两个ByteBuf分别写入缓冲区ChannelOutboundBuffer即可,没必要使 用组合ByteBuf。但是在将ByteBuf写入缓冲区之前,需要对整个消息进行编码,如长度编码,此时需要把两个ByteBuf合并成一个,无须额 外处理就可以知道其整体长度。因此使CompositeByteBuf是非常适合的。

在解码时,由于Socket通信传输数据会产生粘包和半包问题,因 此需要一个读半包字节容器,这个容器采用CompositeByteBuf比较合 适,将每次从Socket中读到的数据直接放入此容器中,少了一次数据 的拷贝。Netty的解码类ByteToMessageDecoder默认的读半包字节容器Cumulator 未 采 CompositeByteBuf , 此 时 可 在 其 子 类 中 调 用setCumulator进行修改。但需要注意的是,CompositeByteBuf需要依 赖具体的使用场景。因为CompositeByteBuf使用了复杂的算法逻辑,所以其效率有可能比使用内存拷贝的低

在这里插入图片描述

3.5、PooledByteBuf

这 个类继承于AbstractReference CountedByteBuf,其对象主要由内存 池分配器PooledByteBufAllocator创建。

比较常用的实现类有两种: 一种是基于堆外直接内存池构建的PooledDirectByteBuf,是Netty在 进行I/O的读/写时的内存分配的默认方式,堆外直接内存可以减少内 存 数 据 拷 贝 的 次 数 ; 另 一 种 是 基 于 堆 内 内 存 池 构 建 的PooledHeapByteBuf。

除 了 上 述 两 种 实 现 类 **, Netty 还 使 用 Java 的 后 门 类
sun.misc.Unsafe实现了两个缓冲区即PooledUnsafeDirectByteBuf和PooledUnsafeHeapByteBuf**。这个强大的后门类会暴露对象的底层地址,一般不建议使用,Netty为了优化性能引入了Unsafe。PooledUnsafeDirectByteBuf会暴露底层DirectByteBuffer的地址memoryAddress,而Unsafe则可通过memoryAddress+Index的方式取得对应的字节。

PooledUnsafeHeapByteBuf也会暴露字节数组在Java堆中的地址, 因此不再使用字节数组的索引,即array[index]。同样,Unsafe可通过BYTE_ARRAY_BASE_OFFSET+Index字节的地址获取字节。

由于创建PooledByteBuf对象的开销大,而且在高并发情况下,当 网络I/O进行读/写时会创建大量的实例。因此,为了降低系统开销,Netty对Buffer对象进行了池化,缓存了Buffer对象,使对此类型的Buffer可进行重复利用。PooledByteBuf是从内存池中分配出来的Buffer,因此它需要包含内存池的相关信息,如内存块Chunk、PooledByteBuf在内存块中的位置及其本身所占空间的大小等

在这里插入图片描述

相关文章
|
3月前
|
前端开发 网络协议 Java
Netty | 工作流程图分析 & 核心组件说明 & 代码案例实践
Netty | 工作流程图分析 & 核心组件说明 & 代码案例实践
114 0
|
3月前
|
网络协议 前端开发 Java
Netty Review - 核心组件扫盲
Netty Review - 核心组件扫盲
73 0
|
7月前
|
监控 前端开发 网络协议
Netty核心组件详解
Netty核心组件详解
36 0
|
7月前
|
编解码 前端开发 Java
源码分析Netty:核心组件及启动过程分析
本篇从实例出发,了解Netty核心组件的概念、作用及串联过程。从概念到设计原理,再到深入了解实现细节,从而能够清晰地掌握Netty的技术细节甚至存在的问题,才能最终更好地支持我们实际的各项业务。
314 0
|
8月前
|
前端开发 Java 调度
Netty中有哪些核心组件?
最近又有粉丝问我这样一个问题,说Netty中最核心的组件有哪些?它们都起什么作用?今天,给大家详细聊一聊
53 0
|
设计模式 监控 前端开发
第 10 章 Netty 核心源码剖析
第 10 章 Netty 核心源码剖析
97 0
|
存储 前端开发 Java
【Netty 从成神到升仙系列 一】Netty 服务端的启动源码剖析(一)
【Netty 从成神到升仙系列 一】Netty 服务端的启动源码剖析(一)
【Netty 从成神到升仙系列 一】Netty 服务端的启动源码剖析(一)
|
存储 缓存 数据处理
Netty源码剖析之数据通信流程
NIO事件/感兴趣事件 OP_REGISTER = 0 通道注册事件 OP_READ = 1 << 0 OP_WRITE = 1 << 2 OP_CONNECT = 1 << 3 OP_ACCEPT = 1 << 4
|
编解码
Netty源码剖析之Netty启动流程
了解netty启动流程,有助于学习netty,进行自定义组件扩展
102 0
Netty源码剖析之NIOEventLoopGroup创建流程
Netty中事件循环机制非常重要,通过NIOEventLoopGroup可以了解到netty如何实现处理请求,如何实现事件监听处理,转发,有助于平时学习使用
Netty源码剖析之NIOEventLoopGroup创建流程