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在内存块中的位置及其本身所占空间的大小等

在这里插入图片描述

相关文章
|
7月前
|
设计模式 前端开发 网络协议
面试官:说说Netty的核心组件?
Netty 核心组件是指 Netty 在执行过程中所涉及到的重要概念,这些核心组件共同组成了 Netty 框架,使 Netty 框架能够正常的运行。 Netty 核心组件包含以下内容: 1. 启动器 Bootstrap/ServerBootstrap 2. 事件循环器 EventLoopGroup/EventLoop 3. 通道 Channel 4. 通道处理器 ChannelHandler 5. 通道管道 ChannelPipeline 这些组件的交互流程如下: ![image.png](https://cdn.nlark.com/yuque/0/2024/png/92791/1716
48 0
面试官:说说Netty的核心组件?
|
7月前
|
前端开发 Java 网络安全
【Netty 网络通信】Netty 核心组件
【1月更文挑战第9天】【Netty 网络通信】Netty 核心组件
|
7月前
|
消息中间件 Oracle Dubbo
Netty 源码共读(一)如何阅读JDK下sun包的源码
Netty 源码共读(一)如何阅读JDK下sun包的源码
132 1
|
NoSQL Java Redis
跟着源码学IM(十二):基于Netty打造一款高性能的IM即时通讯程序
关于Netty网络框架的内容,前面已经讲了两个章节,但总归来说难以真正掌握,毕竟只是对其中一个个组件进行讲解,很难让诸位将其串起来形成一条线,所以本章中则会结合实战案例,对Netty进行更深层次的学习与掌握,实战案例也并不难,一个非常朴素的IM聊天程序。 原本打算做个多人斗地主练习程序,但那需要织入过多的业务逻辑,因此一方面会带来不必要的理解难度,让案例更为复杂化,另一方面代码量也会偏多,所以最终依旧选择实现基本的IM聊天程序,既简单,又能加深对Netty的理解。
168 1
|
7月前
|
前端开发 网络协议 Java
Netty | 工作流程图分析 & 核心组件说明 & 代码案例实践
Netty | 工作流程图分析 & 核心组件说明 & 代码案例实践
392 0
|
7月前
|
编解码 前端开发 网络协议
Netty Review - ObjectEncoder对象和ObjectDecoder对象解码器的使用与源码解读
Netty Review - ObjectEncoder对象和ObjectDecoder对象解码器的使用与源码解读
166 0
|
7月前
|
编解码 安全 前端开发
Netty Review - StringEncoder字符串编码器和StringDecoder 解码器的使用与源码解读
Netty Review - StringEncoder字符串编码器和StringDecoder 解码器的使用与源码解读
260 0
|
7月前
|
网络协议 前端开发 Java
Netty Review - 核心组件扫盲
Netty Review - 核心组件扫盲
101 0
|
分布式计算 网络协议 前端开发
【Netty底层数据交互源码】
【Netty底层数据交互源码】
|
Java 容器
【深入研究NIO与Netty线程模型的源码】
【深入研究NIO与Netty线程模型的源码】