从 Spring Boot 程序启动深入理解 Netty 异步架构原理( 四 )

  • 复合缓冲区,顾名思义就是将上述两类缓冲区聚合在一起 。Netty 提供了一个 CompsiteByteBuf,可以将堆缓冲区和直接缓冲区的数据放在一起,让使用更加方便 。
  • ByteBuf 的分配
    聊完了结构和使用模式,再来看看 ByteBuf 是如何分配缓冲区的数据的 。
    Netty 提供了两种 ByteBufAllocator 的实现,他们分别是:
    • PooledByteBufAllocator,实现了 ByteBuf 的对象的池化,提高性能减少内存碎片 。
    • Unpooled-ByteBufAllocator,没有实现对象的池化,每次会生成新的对象实例 。
    对象池化的技术和线程池,比较相似,主要目的是提高内存的使用率 。池化的简单实现思路,是在 JVM 堆内存上构建一层内存池,通过 allocate 方法获取内存池中的空间,通过 release 方法将空间归还给内存池 。
    对象的生成和销毁,会大量地调用 allocate 和 release 方法,因此内存池面临碎片空间回收的问题,在频繁申请和释放空间后,内存池需要保证连续的内存空间,用于对象的分配 。
    基于这个需求,有两种算法用于优化这一块的内存分配:伙伴系统和 slab 系统 。
    伙伴系统,用完全二叉树管理内存区域,左右节点互为伙伴,每个节点代表一个内存块 。内存分配将大块内存不断二分,直到找到满足所需的最小内存分片 。
    内存释放会判断释放内存分片的伙伴(左右节点)是否空闲,如果空闲则将左右节点合成更大块内存 。
    slab 系统,主要解决内存碎片问题,将大块内存按照一定内存大小进行等分,形成相等大小的内存片构成的内存集 。
    按照内存申请空间的大小,申请尽量小块内存或者其整数倍的内存,释放内存时,也是将内存分片归还给内存集 。
    Netty 内存池管理以 Allocate 对象的形式出现 。一个 Allocate 对象由多个 Arena 组成,每个 Arena 能执行内存块的分配和回收 。
    Arena 内有三类内存块管理单元:
    • TinySubPage
    • SmallSubPage
    • ChunkList
    Tiny 和 Small 符合 Slab 系统的管理策略,ChunkList 符合伙伴系统的管理策略 。
    当用户申请内存介于 tinySize 和 smallSize 之间时,从 tinySubPage 中获取内存块 。
    申请内存介于 smallSize 和 pageSize 之间时,从 smallSubPage 中获取内存块;介于 pageSize 和 chunkSize 之间时,从 ChunkList 中获取内存;大于 ChunkSize(不知道分配内存的大小)的内存块不通过池化分配 。
    Netty 的 Bootstrap说完了 Netty 的核心组件以及数据存储 。再回到最开始的例子程序,在程序最开始的时候会 new 一个 Bootstrap 对象,后面所有的配置都是基于这个对象展开的 。
    从 Spring Boot 程序启动深入理解 Netty 异步架构原理

    文章插图
    生成 Bootstrap 对象
    Bootstrap 的作用就是将 Netty 核心组件配置到程序中,并且让他们运行起来 。
    从 Bootstrap 的继承结构来看,分为两类分别是 Bootstrap 和 ServerBootstrap,一个对应客户端的引导,另一个对应服务端的引导 。
    从 Spring Boot 程序启动深入理解 Netty 异步架构原理

    文章插图
    支持客户端和服务端的程序引导
    客户端引导 Bootstrap,主要有两个方法 bind() 和 connect() 。Bootstrap 通过 bind() 方法创建一个 Channel 。
    在 bind() 之后,通过调用 connect() 方法来创建 Channel 连接 。
    从 Spring Boot 程序启动深入理解 Netty 异步架构原理

    文章插图
    Bootstrap 通过 bind 和 connect 方法创建连接
    服务端引导 ServerBootstrap,与客户端不同的是在 Bind() 方法之后会创建一个 ServerChannel,它不仅会创建新的 Channel 还会管理已经存在的 Channel 。
    从 Spring Boot 程序启动深入理解 Netty 异步架构原理

    文章插图
    ServerBootstrap 通过 bind 方法创建/管理连接
    通过上面的描述,服务端和客户端的引导存在两个区别:
    • ServerBootstrap(服务端引导)绑定一个端口,用来监听客户端的连接请求 。而 Bootstrap(客户端引导)只要知道服务端 IP 和 Port 建立连接就可以了 。
    • Bootstrap(客户端引导)需要一个 EventLoopGroup,但是 ServerBootstrap(服务端引导)则需要两个 EventLoopGroup 。因为服务器需要两组不同的 Channel 。第一组 ServerChannel 自身监听本地端口的套接字 。第二组用来监听客户端请求的套接字 。

    从 Spring Boot 程序启动深入理解 Netty 异步架构原理

    文章插图
    ServerBootstrap 有两组 EventLoopGroup
    总结我们从 NIO 入手,谈到了 Selector 的核心机制 。然后通过介绍 Netty 客户端和服务端源代码运行流程,让大家对 Netty 编写代码有基本的认识 。


    推荐阅读