Netty 简介

1 什么是netty

Netty是一款卓越的java框架,提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。它用较简单的抽象,隐藏 Java网络编程底层实现的复杂性。

1.1 Netty 整体结构

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

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

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

1.2 Netty的特性

分类 Netty特性
设计 多协议支持:Netty提供了丰富的协议支持,包括常用的网络协议(如HTTP、WebSocket、TCP和UDP)以及自定义协议。具备灵活的编解码器和处理器,简化了协议的实现和交互。针对多种传输类型的统一接口,支持阻塞和非阻塞简单但更强大的线程模型真正的无连接的数据报套接字支持链接逻辑支持复用可扩展和灵活:Netty的架构和组件设计具有高度的可扩展性和灵活性。它提供了一组可重用的组件,可以根据应用需求进行定制和扩展。
易于使用 完善的JavaDoc,用户指南和样例简洁简单
性能 高性能:Netty采用了一系列优化策略,如零拷贝技术、内存池和可定制的线程模型等,以提供出色的性能和吞吐量。能处理高负载和大规模并发 比核心 Java API 更好的吞吐量,较低的延时Netty能够高效地处理并发连接和大量的并发请求。资源消耗更少,这个得益于共享池和重用减少内存拷贝
健壮性 消除由于慢,快,或重载连接产生的 OutOfMemoryError消除经常发现,在 NIO 在高速网络中的应用中的不公平的读/写比
安全性 完整的 SSL / TLS 和 StartTLS 的支持可运行在受限的环境例如 Applet 或 OSGI
社区驱动

2. 核心组件

Netty的核心组件包括三类:

  • 引导类
  • 针对网络抽象的组件
  • 管理数据流及执行应用程序处理逻辑的组件

2.1 引导类

Bootstrap: 用于客户端,连接到远程主机和端口,1个EventLoopGroup

ServerBootstrap: 用于服务器,绑定到一个本地端口,2个EventLoopGroup

2.2 针对网络抽象的组件:

Channel: 对应Socket。基本的IO操作(bind(), connet(), read(), write()), 依赖于底层网络传输,大大降低了直接使用Socket的复杂性。Channel接口常用的实现类有:EmbeddedChannel, LocalServerChannel, NioDatagramChannel, NioSctpChannel, NioSocketChannel

EventLoop: 控制流、多线程处理、并发,用于处理连接的生命周期中所发生的事件。

ChannelFuture: 异步通知。Netty提供了ChannelFuture接口,通过addListener()方法注册ChannelFutureListener,以便在操作完成时得到通知。

2.3 管理数据流及执行应用程序处理逻辑的组件

ChannelHandler: 处理入站和出站数据。ChannelInboundHandler处理入站操作(常见的实现类:SimpleChannelInboundHandler ), ChannelOutboundHandler处理出站操作。程序员处理出入站数据的应用程序逻辑,都在ChannelHandler接口的实现里。

ChannelPipeline: 提供了 ChannelHandler 链的容器,并定义了用于在该链上传播人站和出站事件流的 API。当Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。ChannelHandler 安装到 ChannelPipeline 中的过程如下所示:

  • 一个channelInitializer的实现被注册到了ServerBootstrap中
  • 当channelInitializer.initChannel()方法被调用时,ChannelInitializer将在ChannelPipeline 中安装一组自定义的 ChannelHandler;
  • ChannelInitializer 将它自己从 ChannelPipeline 中移除。

3. Netty 逻辑架构

Netty 的逻辑处理架构为典型网络分层架构设计,共分为网络通信层、事件调度层、服务编排层,每一层各司其职。

3.1 网络通信层

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

网络通信层的核心组件包含BootStrap、ServerBootStrap、Channel三个组件。

3.1.1 BootStrap & ServerBootStrap

Bootstrap 是“引导”的意思,它主要负责整个 Netty 程序的启动、初始化、服务器连接等过程,它相当于一条主线,串联了 Netty 的其他核心组件。Netty 中的引导器共分为两种类型:一个为用于客户端引导的 Bootstrap,另一个为用于服务端引导的 ServerBootStrap,它们都继承自抽象类 AbstractBootstrap。

Bootstrap 和 ServerBootStrap 十分相似,两者非常重要的区别在于 Bootstrap 可用于连接远端服务器,只绑定一个 EventLoopGroup。而 ServerBootStrap 则用于服务端启动绑定本地端口,会绑定两个 EventLoopGroup,这两个 EventLoopGroup 通常称为 Boss 和 Worker。

ServerBootStrap 中的 Boss 和 Worker 是什么角色呢?它们之间又是什么关系?这里的 Boss 和 Worker 可以理解为“老板”和“员工”的关系。每个服务器中都会有一个 Boss,也会有一群做事情的 Worker。Boss 会不停地接收新的连接,然后将连接分配给一个个 Worker 处理连接。

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

3.1.2 Channel

Channel 的字面意思是“通道”,它是网络通信的载体。Channel提供了基本的 API 用于网络 I/O 操作,如 register、bind、connect、read、write、flush 等。Netty 自己实现的 Channel 是以 JDK NIO Channel 为基础的,相比较于 JDK NIO,Netty 的 Channel 提供了更高层次的抽象,同时屏蔽了底层 Socket 的复杂性,赋予了 Channel 更加强大的功能,你在使用 Netty 时基本不需要再与 Java Socket 类直接打交道。

常用的 Channel 实现类

AbstractChannel 是整个家族的基类,派生出 AbstractNioChannel、AbstractOioChannel、AbstractEpollChannel 等子类,每一种都代表了不同的 I/O 模型和协议类型。常用的 Channel 实现类有:

  • NioServerSocketChannel 异步 TCP 服务端, 最常用的Server Channel。
  • NioSocketChannel 异步 TCP 客户端。最常用的Client Channel。
  • NioDatagramChannel 异步 UDP 连接。
  • OioServerSocketChannel 同步 TCP 服务端。 @Deprecated
  • OioSocketChannel 同步 TCP 客户端。@Deprecated
  • OioDatagramChannel 同步 UDP 连接。@Deprecated

channle的生命周期

Channel 会有多种状态,如连接建立、连接注册、数据读写、连接销毁等。随着状态的变化,Channel 处于不同的生命周期,如下图。每一种状态都会绑定相应的事件回调

Channel 最常见的状态所对应的事件回调如下:

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

3.1.3 网络通讯层总结

简单地总结一下。BootStrap 和 ServerBootStrap 分别负责客户端和服务端的启动,它们是非常强大的辅助工具类;Channel 是网络通信的载体,提供了与底层 Socket 交互的能力。那么 Channel 生命周期内的事件都是如何被处理的呢?那就是 Netty 事件调度层的工作职责了。

3.2 事件调度层

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

3.2.1 EventLoopGroup、EventLoop

EventLoopGroup 本质是一个线程池,主要负责接收 I/O 请求,并分配线程执行处理请求。

一个 EventLoopGroup 往往包含一个或者多个 EventLoop。EventLoop 用于处理 Channel 生命周期内的所有 I/O 事件,如 accept、connect、read、write 等 I/O 事件。

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

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

3.2.2 线程模型

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

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

在介绍完事件调度层之后,可以说 Netty 的发动机已经转起来了,事件调度层负责监听网络连接和读写操作,然后触发各种类型的网络事件,需要一种机制管理这些错综复杂的事件,并有序地执行

3.3 服务编排层

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

服务编排层的核心组件包括 ChannelPipelineChannelHandler、ChannelHandlerContext

3.3.1 ChannelPipeline

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

每一个新的 Channel 都会对应绑定一个新的 ChannelPipeline。一个 ChannelPipeline 关联一个 EventLoop,一个 EventLoop 仅会绑定一个线程。

ChannelPipeline 中包含入站 ChannelInboundHandler 和出站 ChannelOutboundHandler 两种处理器,我们结合客户端和服务端的数据收发流程来理解 Netty 的这两个概念。

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

3.3.2 ChannelHandler & ChannelHandlerContext

数据的编解码工作以及其他转换工作实际都是通过 ChannelHandler 处理的。站在开发者的角度,最需要关注的就是 ChannelHandler,我们很少会直接操作 Channel,都是通过 ChannelHandler 间接完成。

下图描述了 Channel 与 ChannelPipeline 的关系,从图中可以看出,每创建一个 Channel 都会绑定一个新的 ChannelPipeline,ChannelPipeline 中每加入一个 ChannelHandler 都会绑定一个 ChannelHandlerContext。

ChannelHandlerContext 用于保存 ChannelHandler 上下文,通过 ChannelHandlerContext 我们可以知道 ChannelPipeline 和 ChannelHandler 的关联关系。ChannelHandlerContext 可以实现 ChannelHandler 之间的交互,ChannelHandlerContext 包含了 ChannelHandler 生命周期的所有事件,如 connect、bind、read、flush、write、close 等。

3.4 组件关系梳理

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

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

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

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

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

官网:

Netty: Home

好文:

Netty核心概念、架构及用法 - 知乎

超详细Netty入门,看这篇就够了! - 知乎

你可能感兴趣的:(java,netty)