一、整理架构

下图是Netty官网上给出的整体功能模块:

1. Core 核心层

Core核心层是Netty最精华的部分了,它提供了底层网络通信的抽象和实现,包括可扩展的事件模式、通用的通信API和支持零拷贝的ByteBuf

2. Protocol Support 协议支持层

协议支持层上基本覆盖了现在主流协议的编解码实现,例如HTTP、WebSocket、SSL、Protobuf、压缩、大文件传输等主流协议,此外Netty还支持自定义应用层协议。Netty丰富的协议支持降低了使用的开发成本,基于Netty可以快速开始HTTP、WebSocket等服务。

3. Transport Service 传输服务层

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

Netty的模块化设计具有较高的通用性和可扩展性,Netty不仅是一个有效的网络框架,还可以作为网络编程的工具箱。

二、Netty 的逻辑架构

Netty 的逻辑处理架构是典型的网络分层架构设计的,分别为网络通信层、事件调度层和服务编排层。每一层各司其职,如下图所示:

1. 网络通信层

网络通信层的职责就是执行网络I/O的操作。它支持多种网络协议和I/O模型的连接操作。当网络数据读取到内核缓冲区后,会触发各种网络事件,这些网络事件会分发给事件调度层来处理。网络通信层有三个核心组件:Bootstrap、ServerBootstrap和Channel。

  • Bootstrap 和 ServerBootstrap

    Bootstrap和ServerBootstrap都继承于AbstractBootstrap,在功能上很相似。Bootstrap是用于连接服务端的,一般用于Client的开发上;ServerBootstrap则是用于服务端启动绑定本地端口,一般绑定两个EventLoopGroup(主从多线程Reactor),一个称为boss,另外一个成为worker。

    Bootstrap/ServerBootstrap组件更加方便我们配置和启动Netty程序,它是整个Netty程序的入口,串联了Netty所有核心组件的初始化工作。

  • Channel

    它是Netty网络通信的载体。Channel提供了基本的API用于网络I/O操作,例如register注册事件、bind绑定事件、connect连接事件、read读事件、write写事件、flush数据冲刷等。

    Netty中的Channel是基于JDK NIO的Channel上做了更高层次的抽象,同时屏蔽了底层Socket的复杂性,增强了Channel的功能,再使用Netty时时不需要再跟JDK Socket打交道。

    在下图可以看到Netty中的Channel的实现:

    • NioServerSocketChannel 异步TCP服务端

    • NioSocketChannel 异步TCP客户端

    • OioServerSocketChannel 同步TCP服务端

    • OioSocketChannel 同步TCP客户端

    • NioDatagramChannel 异步UDP连接

    • OioDatagramChannel 同步UDP连接

    Channel事件状态:

    事件 说明
    channelRegistered Channel创建后被注册到EventLoop上
    channelUnregistered Channel创建后为注册或从EventLoop上取消注册
    channelActive Channel处于就绪状态,可以被读写
    channelInactive Channel处于非就绪状态
    channelRead Channel可以从远端读取到数据
    channelReadComplete Channel读取数据完成

2. 事件调度层

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

事件调度层最核心的组件就是EventLoop和EventLoopGroup:EventLoopGroup实质上是Netty基于JDK线程池的抽象,本质就是线程池,主要负责接收I/O请求,并分配线程执行处理。而EventLoop可以理解成一个线程,EventLoop创建出来之后会跟一个线程绑定并且处理Channel中的事件。如下图所示:

1、一个EventLoopGroup包含一个或者多个EventLoop。EventLoop用于处理Channel生命周期的所有I/O事件。

2、EventLoop在同一时间只会跟一个线程绑定。每个EventLoop负责处理多个Channel。

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

Netty中不同的I/O模型有对应的EventLoop实现,下面是常用NioEventLoop的家族图谱:

EventLoopGroup是Netty Reactor线程模型的具体实现方式,Netty通过创建不同的EventLoopGroup参数配置就可以支持Reactor的三种线程模型:

  1. Reactor单线程模型:EventLoopGroup 只包含一个 EventLoop,Boss 和 Worker 使用同一个EventLoopGroup

  2. 非主从Reactor多线程模型:EventLoopGroup 包含多个 EventLoop,Boss 和 Worker 使用同一个EventLoopGroup

  3. 主从Reactor多线程模型:EventLoopGroup 包含多个 EventLoop,Boss 是主 Reactor,Worker 是从 Reactor,它们分别使用不同的 EventLoopGroup,主 Reactor 负责新的网络连接 Channel 创建,然后把 Channel 注册到从 Reactor

3. 服务编排层

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

服务编排的核心组件有:ChannelPipeline、ChannelHandler、ChannelHandlerContext。

1、ChannelPipeline

Netty的核心编排组件,负责组装各类的ChannelHandler,实际数据的编解码以及加工处理的操作是由ChannelHandler来完成的。ChannelPipeline内部通过双向链表将不同的ChannelHandler链接在一起。当I/O读写事件触发时,ChannelPipeline会一次调用ChannelHandler列表对Channel的数据进行拦截和处理。

ChannelPipeline是线程安全的,因为每一个新的Channel都会对应一个新的ChannelPipeline。一个ChannelPipeline关联一个EventLoop,一个EventLoop只会绑定一个线程。如下如图截取来自ChannelPipeline类注释中:

可以看得出来,ChannelPipeline中包含入站ChannelInboundHandler和出战的ChannelOutboundHandler两种处理器,结合服务端和客户端的数据收发来看,如下图:

客户端和服务端都有自己的ChannelPipeline。从客户端的角度来看,数据从客户端发向服务端,该过程称为出站,反之则称为入站。数据入站会由一系列InBoundHandler 处理,然后再以相反方向的 OutBoundHandler 处理后完成出站。我们经常使用的编码 Encoder 是出站操作,解码 Decoder 是入站操作。服务端接收到客户端数据后,需要先经过 Decoder 入站处理后,再通过 Encoder 出站通知客户端。所以客户端和服务端一次完整的请求应答过程可以分为三个步骤:客户端出站(请求数据)、服务端入站(解析数据并执行业务逻辑)、服务端出站(响应结果)。

2、ChannelHandler 和 ChannelHandlerContext

每创建一个 Channel 都会绑定一个新的 ChannelPipeline,ChannelPipeline 中每加入一个 ChannelHandler 都会绑定一个 ChannelHandlerContext。由此可见,ChannelPipeline、ChannelHandlerContext、ChannelHandler 三个组件的关系是密切相关的。

ChannelHandlerContext 用于保存 ChannelHandler 上下文,通过 ChannelHandlerContext 我们可以知道 ChannelPipeline 和 ChannelHandler 的关联关系。ChannelHandlerContext 可以实现 ChannelHandler 之间的交互,ChannelHandlerContext 包含了 ChannelHandler 生命周期的所有事件,如 connect、bind、read、flush、write、close 等。此外,你可以试想这样一个场景,如果每个 ChannelHandler 都有一些通用的逻辑需要实现。其实在很多的框架中都存在这种上下文对象的抽象。

如下如图:

三、总结

  • 服务端启动初始化时有 Boss EventLoopGroup 和 Worker EventLoopGroup 两个组件,其中 Boss 负责监听网络连接事件。当有新的网络连接事件到达时,则将 Channel 注册到 Worker EventLoopGroup。

  • Worker EventLoopGroup 会被分配一个 EventLoop 负责处理该 Channel 的读写事件。每个 EventLoop 都是单线程的,通过 Selector 进行事件循环。

  • 当客户端发起 I/O 读写事件时,服务端 EventLoop 会进行数据的读取,然后通过 Pipeline 触发各种监听器进行数据的加工处理。

  • 客户端数据会被传递到 ChannelPipeline 的第一个 ChannelInboundHandler 中,数据处理完成后,将加工完成的数据传递给下一个 ChannelInboundHandler。

  • 当数据写回客户端时,会将处理结果在 ChannelPipeline 的 ChannelOutboundHandler 中传播,最后到达客户端。