写点什么

Netty 系列教程(二)Netty 架构设计剖析

作者:Java-fenn
  • 2022 年 9 月 12 日
    湖南
  • 本文字数:3346 字

    阅读完需:约 11 分钟

Netty 整体架构

这是一张 Netty 官网关于 Netty 的核心架构图:



netty 架构图

可以看到 Netty 由以下三个核心部分构成:

  • 传输服务:传输服务层提供了网络传输能力的定义和实现方法。它支持 Socket、HTTP 隧道、虚拟机管道等传输方式。Netty 对 TCP、UDP 等数据传输做了抽象和封装,用户可以更聚焦在业务逻辑实现上,而不必关系底层数据传输的细节。\

  • 协议支持:Netty 支持多种常见的数据传输协议,包括:HTTP、WebSocket、SSL、zlib/gzip、二进制、文本等,还支持自定义编解码实现的协议。Netty 丰富的协议支持降低了开发成本,基于 Netty 我们可以快速开发 HTTP、WebSocket 等服务。\

  • Core 核心:Netty 的核心,提供了底层网络通信的通用抽象和实现,包括:可扩展的事件驱动模型、通用的通信 API、支持零拷贝的 Buffer 缓冲对象。\

Netty 逻辑架构

Netty 采用典型的三层网络架构进行设计和开发,其逻辑架构如图:



netty 逻辑架构

网络通信层

网络通信层的职责主要是监听网络的读写和连接操作,负责将网络层的数据读取到内存缓冲区中,然后触发各种网络事件,如连接创建、连接激活、读事件、写事件等,这些网络事件会分发给事件调度层进行处理。

网络通信层的核心组件有:

  • Bootstrap:启动引导,主要负责整个 Netty 程序的启动、初始化、服务器连接等过程,它相当于一条主线,把 Netty 的其他核心组件给串联起来。Netty 中的引导器分为两种:Bootstrap:客户端启动引导,另一种是 ServerBootstrap:服务端启动引导。它们都继承自抽象类 AbstractBootstrap。\

  • Channel:网络通信的载体,提供了基本的用于 I/O 操作的 API,如:register、bind、connect、read、write、flush 等。Netty 的 Channel 是在 JDK 的 NIO Channel 基础上进行封装的,提供了更高层次的抽象,同时屏蔽了底层 Socket 的复杂性,赋予了 Channel 更加强大的功能。\

事件调度层

事件调度层的职责是通过 Reactor 线程模型对各类事件进行聚合处理,通过 Selector 主循环线程集成多种事件( I/O 事件、信号事件、定时事件等),实际的业务处理逻辑是交由服务编排层中相关的 Handler 完成。

事件调度层的核心组件有:

  • EventLoopGroup

  • EventLoop

EventLoopGroup 本质上是一个线程池,主要负责接收 I/O 请求,并分配线程处理请求。一个 EventLoopGroup 包含一个或者多个 EventLoop,EventLoop 用于处理 Channel 生命周期内的所有 I/O 事件,如 accept、read、write 等。

EventLoop 同一时间会与一个线程绑定,每个 EventLoop 负责处理多个 Channel。

每新建一个 Channel,EventLoopGroup 会选择一个 EventLoop 与其绑定,该 Channel 生命周期内都可以对 EventLoop 进行多次绑定和解绑。

服务编排层

负责组装各类服务,它是 Netty 的核心业务处理链,用于实现网络事件的动态编排和有序传播。

服务编排层的核心组件有:

  • ChannelPipeline:handler 处理器链列表,也可以说是容器,内部通过双向链表将不同的 ChannelHandler 串联在一起,当 I/O 读写事件发生时,ChannelPipeline 会依次调用 ChannelHandler 列表对 Channel 的数据进行拦截处理。每个 Channel 创建时就会绑定一个新的 ChannelPipeline,他们是一一对应的关系。

  • ChandlerHandler:事件业务处理器顶层接口,它不处理入站和出站事件,而是由其子类来实现,核心子类包括:

  • ChannelInboundHandler:负责处理入站事件,以及用户自定义事件

    ChannelOutboundHandler:负责处理出站事件

    ChannelDuplexHandler:同时实现了 ChannelInboundHandler 和 ChannelOutboundHandler 两个接口,既可以处理入站事件,也可以处理出站事件

  • ChannelHandlerContext:ChandlerHandler 的上下文对象,在调用 ChandlerHandler 方法时,都会传入该上下文对象,ChannelHandlerContext 包含了 ChannelHandler 生命周期的所有事件,如 connect、bind、read、flush、write、close 等。

由于 Netty 的分层架构设计合理、优雅,因此,基于 Netty 开发的各种服务器程序才会越来越多。

Netty 架构特点

高性能

Netty 有着极高的性能,能够轻松应对高并发场景,并保证性能,这得益于它优良精细的架构设计,如果一个产品的架构设计得不好,无论开发如何努力,都很难开发出一个高性能、高可用的产品。

Netty 通过如下设计,来保证系统的高性能:

IO 模型

用什么样的通道将数据发送给对方,I/O 模型在很大程度上决定了框架的性能。

典型的有三种 IO 模型分别是 BIO、NIO、AIO,Netty 采用的是 NIO 模型来实现。具体三种 IO 模型的区别在第一章中有介绍。

Netty 的 IO 线程 NioEventLoop 由于聚合了多路复用器 Selector,可以同时处理成百上千个客户端连接。

当线程从某客户端 socket 通道进行读写数据时,若没有数据读写事件,则该线程可以处理其他任务,线程通常将非阻塞的 IO 的空闲时间用于其他通道上执行 I/O 操作,所以单独的线程可以管理多个输入和输出通道。

由于读写操作都是非阻塞的,这就可以充分提升 IO 线程的运行效率,避免由于频繁 I/O 阻塞导致的线程挂起。

一个 I/O 线程可以并发处理 N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞 I/O 一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。

线程模型

Netty 采用的是 Reactor 线程模型,常见的 Reactor 线程模型有:

  • 单 Reactor 单线程模型

  • 单 Reactor 多线程模型

  • 主从 Reactor 多线程模型

Netty 主要基于主从 Reactor 多线程模型来实现其线程模型,主 Reactor 主要负责客户端的连接请求,并将请求转发给从 Reactor;从 Reactor 主要负责响应通道的 IO 读写请求。



netty 线程模型

内存优化

TCP 接收和发送缓冲区使用直接内存代替堆内存,避免了内存复制,避免了 GC 带来的性能开销,提升了 IO 读取和写入的性能。

多线程优化

采用环形数组缓冲区实现无锁化并发编程,代替传统的线程安全容器或者锁。并且关键资源的处理使用 单线程串行化方式,避免多线程并发访问带来的锁竞争和额外的 CPU 资源消耗问题。

高可靠性

链路有效性检测

由于长链接不需要每次发送消息都创建链路,也不需要在消息交互完成时关闭链路,因此相对于短链接性能更高。为了保证长链接的链路有效性, 往往需要通过心跳机制周期性地进行链路检测,当链路失效时,可以及时关闭链路。

当有业务消息时,无需心跳检测,可以通过业务进行链路检测。所以心跳检测只需要在链路空闲时发挥作用。

为了支持心跳检测,Netty 提供了如下三种链路空闲检测机制:

  • 读空闲超时机制:当连续周期 T 没有消息可读时,触发超时 handler,用户可以基于读空闲超时发送心跳消息,进行链路检测

  • 写空闲超时机制:当连续周期 T 没有消息要发送时,触发超时 handler,用户可以基于写空闲超时发送心跳消息,进行链路检测

  • 读写空闲超时机制:当连续周期 T 没有消息要读/写时,触发超时 handler,用户可以基于写空闲超时发送心跳消息,进行链路检测,其中只要读或者写任意一个达到条件就可以触发超时 handler

Netty API 提供了灵活的心跳超时检测配置能力,用户可以配置超时策略、超时参数、以及对应的处理逻辑。

内存保护机制

Netty 提供了多种机制来对内存进行保护,包括以下几个方面:

  • 通过对象引用计数器对 Netty 的 ByteBuffer 等内置对象进行细粒度的内存申请和释放,对非法的对象引用进行检测和保护

  • 通过内存池来重用 ByteBuffer,节省内存

  • 可设置的内存容量上限,包括 ByteBuf、线程池线程数等

优雅停机

优雅停机功能指当前系统退出时,JVM 通过注册的 Shutdown Hook 拦截到退出信号量,然后执行退出操作,释放相关模块的资源占用,将缓冲区的消息处理完或者清空,将待刷新的数据持久化到磁盘或者数据库中,等到资源回收和缓冲区消息处理完成之后再退出。

Netty 在所有涉及到资源回收和释放的地方都增加了优雅退出的方法,比如:

//关闭资源bossGroup.shutdownGracefully();复制代码
复制代码

高可扩展性

API 中提供了大量的扩展点

Netty 的 API 设计时,提供了大量的扩展点,方面用户扩展自定义的逻辑处理,包括:

  • 责任链模式:ChannelPipeline 基于责任链模式开发,便于业务逻辑拦截定制扩展

  • 基于接口的开发:关键的类库都提供了接口或者抽象类,如果 Netty 自身的实现无法满足需求,可以自定义实现相关接口\

  • 提供了大量工厂类,通过重载这些工厂类可以按需创建出用户实现的对象

  • 提供了大量的系统参数供用户按需设置, 增强系统的场景定制能力

多种协议支持

Netty 支持各种主流的应用协议,如 HTTP、FTP、WebSocket 等,也可以基于 Netty 的二进制类库实现协议的扩展和定制。

目前存在大量基于 Netty 框架开发的协议,如:Dubbo 协议、RocketMQ 内部私有协议等。


用户头像

Java-fenn

关注

需要Java资料或者咨询可加我v : Jimbye 2022.08.16 加入

还未添加个人简介

评论

发布
暂无评论
Netty系列教程(二)Netty架构设计剖析_Java_Java-fenn_InfoQ写作社区