Netty逻辑架构与核心组件
Netty逻辑架构与核心组件
Netty官网 里有一张 Netty 的整体功能模块结构图,如下:

Netty 被分为三大模块,分别是 Core、Protocol Support 和 Transport Services。
- Core 即核心层,提供了底层网络通信的通用抽象和实现,包括可扩展的事件模型、通用的通信 API、支持零拷贝的 ByteBuffer 等。
- Protocol Support 即协议支持层,基本上覆盖了主流协议的编解码实现,如 HTTP、 SSL、 Protobuf、压缩、大文件传输、 WebSocket、文本、二进制等主流协议,此外 Netty 还支持自定义应用层协议。
- Transport Services 即传输服务层,提供了网络传输能力的定义和实现方法。支持 Socket、 HTTP 隧道、虚拟机管道等传输方式。 Netty 对 TCP、 UDP 等数据传输做了抽象和封装,用户可以更聚焦在业务逻辑实现上,而不必关系底层数据传输的细节。
这是官网对 Netty 主要模块的总结,接下来介绍 Netty 的逻辑架构。
逻辑架构
以 自定义通信协议实践 的服务端启动代码为例:
1 | |
其中涉及到的组件几乎都在下面这张图里有体现:

Netty 逻辑架构可以分为网络通信层、事件调度层和服务编排层。
网络通信层
网络通信层的职责是执行网络 I/O 的操作。它支持多种网络协议和 I/O 模型的连接操作。当网络数据读取到内核缓冲区后,会触发各种网络事件,这些网络事件会分发给事件调度层进行处理。
网络通信层的 核心组件 包含 BootStrap、ServerBootStrap、Channel 三个组件。
BootStrap 译为引导器,作为整个 Netty 客户端和服务端的程序入口(客户端使用 BootStrap,服务端使用 ServerBootStrap),可以把 Netty 的核心组件像搭积木一样组装在一起。
ServerBootstrap 需要 配置Reactor模型 使用,也就是图中的 EventLoopGroup,其中 MainReactor 处理连接事件,通常叫 BossGroup,SubReactor 处理其它 IO 事件,通常叫 WorkGroup。
Channel 的字面意思是“通道”,它是网络通信的载体,提供了与底层 Socket 交互的能力。Channel 会有多种状态,如 连接建立、连接注册、数据读写、连接销毁 等,每一种状态都会绑定相应的事件回调。

事件调度层
事件调度层的职责是通过 Reactor 线程模型对各类事件进行聚合处理,通过 Selector 主循环线程集成多种事件( I/O 事件、信号事件、定时事件等),实际的业务处理逻辑是交由服务编排层中相关的 Handler 完成。
事件调度层的 核心组件 包括 EventLoopGroup、EventLoop。
EventLoopGroup 本质是一个 EventLoop 的线程池,主要负责接收 I/O 请求,并分配线程执行处理请求。

在 MainReactor(bossGroup) 与客户端建立连接成功后,会存在一个与客户端进行通信的 Channel,接着这个 Channel 会被注册给 SubReactor(workGroup)中的一个(EventLoop),这个 EventLoop 就负责该客户端的后续事件处理,也就是说一个 EventLoop 会负责多个 Channel 的处理。
从 Netty4 开始 Netty 采用了 无锁串行化 设计,其实就是每个 EventLoop 被设计成单线程的,维护一个 Selector 选择器和任务队列 taskQueue,负责处理所管理的 Socket 的 I/O 事件、普通任务和定时任务。这样的设计避免了线程切换,但是它的缺陷就是不能执行时间过长的 I/O 操作,一旦某个 I/O 事件发生阻塞,那么后续的所有 I/O 事件都无法执行,甚至造成事件积压,因此在 Handler 中如果有耗时的业务操作,需要单独分配业务线程池使用。
EventLoop 不仅负责处理 I/O 事件,还要兼顾执行任务队列中的任务。任务队列遵循 FIFO 规则,可以保证任务执行的公平性。NioEventLoop 处理的任务类型基本可以分为三类。
- 普通任务:通过 NioEventLoop 的 execute() 方法向任务队列 taskQueue 中添加任务。例如 Netty 在写数据时会封装 WriteAndFlushTask 提交给 taskQueue。taskQueue 的实现类是多生产者单消费者队列 MpscChunkedArrayQueue,在多线程并发添加任务时,可以保证线程安全。
- 定时任务:通过调用 NioEventLoop 的 schedule() 方法向定时任务队列 scheduledTaskQueue 添加一个定时任务,用于周期性执行该任务。例如,心跳消息发送等。定时任务队列 scheduledTaskQueue 采用优先队列 PriorityQueue 实现。
- 尾部队列:tailTasks 相比于普通任务队列优先级较低,在每次执行完 taskQueue 中任务后会去获取尾部队列中任务执行。尾部任务并不常用,主要用于做一些收尾工作,例如统计事件循环的执行时间、监控信息上报等。
服务编排层
服务编排层的职责是负责组装各类服务,它是 Netty 的核心处理链,用以实现网络事件的动态编排和有序传播。
服务编排层的 核心组件 包括 ChannelPipeline 、 ChannelHandler、ChannelHandlerContext。
ChannelPipeline 是 Netty 的核心编排组件,负责组装各种 ChannelHandler,实际数据的编解码以及加工处理操作都是由 ChannelHandler 完成的。 ChannelPipeline 可以理解为 ChannelHandler 的实例列表 ——内部通过双向链表将不同的 ChannelHandler 链接在一起。当 I/O 读写事件触发时, ChannelPipeline 会依次调用 ChannelHandler 列表对 Channel 的数据进行拦截和处理。
ChannelPipeline 是线程安全的,因为每一个新的 Channel 都会对应绑定一个新的 ChannelPipeline。一个 ChannelPipeline 关联一个 EventLoop,一个 EventLoop 仅会绑定一个线程(单线程处理单 Channel,且该 Channel 有独立的 ChannelPipeline,自然没有线程安全问题)。
ChannelPipeline 中包含入站 ChannelInboundHandler 和出站 ChannelOutboundHandler 两种处理器,我们结合客户端和服务端的数据收发流程来理解 Netty 的这两个概念。

客户端和服务端都有各自的 ChannelPipeline。以客户端为例,数据从客户端发向服务端,该过程称为 出站,反之则称为 入站。数据入站会由一系列 InBoundHandler 处理,然后再以相反方向的 OutBoundHandler 处理后完成出站。
1 | |
我们经常使用的编码 Encoder 是出站操作,解码 Decoder 是入站操作。服务端接收到客户端数据后,需要先经过 Decoder 入站处理后,再通过 Encoder 出站通知客户端。所以客户端和服务端一次完整的请求应答过程可以分为三个步骤:客户端出站(请求数据)、服务端入站(解析数据并执行业务逻辑)、服务端出站(响应结果)。
不过实际上,ChannelPipeline 中并不是多个的 ChannelHandler,而是多个 ChannelHandlerContext,所有 ChannelHandlerContext 之间组成了双向链表。ChannelHandlerContext 用于保存 ChannelHandler 上下文并包含了 ChannelHandler 生命周期的所有事件,复用前置后置的通用逻辑。
ChannelPipeline 的双向链表分别维护了 HeadContext 和 TailContext 的头尾节点,这两个节点在 Netty 中已经默认实现了,自定义的 ChannelHandler 会插入到 Head 和 Tail 之间。

HeadContext 既是 Inbound 处理器,也是 Outbound 处理器。它分别实现了 ChannelInboundHandler 和 ChannelOutboundHandler。网络数据写入操作的入口就是由 HeadContext 节点完成的。 HeadContext 作为 Pipeline 的头结点负责读取数据并开始传递 InBound 事件,当数据处理完成后,数据会反方向经过 Outbound 处理器,最终传递到 HeadContext,所以 HeadContext 又是处理 Outbound 事件的最后一站。此外 HeadContext 在传递事件之前,还会执行一些前置操作。
TailContext 只实现了 ChannelInboundHandler 接口。它会在 ChannelInboundHandler 调用链路的最后一步执行,主要用于终止 Inbound 事件传播,例如释放 Message 数据资源等。 TailContext 节点作为 OutBound 事件传播的第一站,仅仅是将 OutBound 事件传递给上一个节点。
小结
本篇介绍了 Netty 的逻辑架构和核心组件,并简要讲解了 Netty 处理请求的流程(多 Reactor 模型)。
- bossGroup 中有一个 EventLoop,绑定某个特定端口进行监听。一旦有新的连接进来触发 accept 类型事件,就会在当前 EventLoop 的 I/O 事件处理阶段,将这个连接注册到 workGroup 中的某一个 EventLoop,进行后续事件的监听。
- workGroup (如果不指定线程数,默认cpu核数*2)中的每一个 EventLoop,会通过 selcetor 对绑定到自身的 channel 进行轮询,获取 I/O 事件。
- workGroup 中的每一个 EventLoop 获取到 I/O 事件后,会在事件处理阶段通过对应的 ChannelPipeline 进行处理。
- workGroup 中的每一个 EventLoop 每次 select() 循环处理完 I/O 事件后,还会处理其任务队列(taskQueue)的任务(不一定执行完,有超时时间,避免影响下一轮的 I/O 事件的处理)。