目录
前言
原生NIO VS Netty
原生NIO存在的问题
Netty的优点
线程模型
传统阻塞 I/O (Blocking I/O)
2. 非阻塞 I/O (Non-blocking I/O)
3. 多路复用 I/O (Multiplexed I/O)
4. Reactor 模式
常见的 Reactor 模式的变体:
Netty线程模型
工作原理
前言
Netty是一个高性能、异步、高可靠性,基于事件驱动的网络应用程序框架和工具,用于快速开发可维护的高性能和高可扩展性的协议服务器和客户端,换句话说,Netty是一个基于NIO客户端服务器框架,它简化Java 原生NIO开发难度,是目前最流行的NIO框架,Netty在互联网应用领域、游戏领域、大数据分布式计算领域都有广泛的应用,比如分布式服务框架Dobbo,分布式检索引擎ElasticSearch,大数据计算引擎Spark、Flink等都采用Netty作为网络通信的框架。
原生NIO VS Netty
原生NIO存在的问题
- 复杂性:
-
- NIO设计相对复杂,开发者需要处理许多底层细节,如缓冲区管理,通道操作等。
- 需要手动管理资源,容易出现资源泄露。
- 缺乏直观API设计
- 开发工作量大:
-
- NIO涉及多线程和异步编程,学习成本高,开发者需要更多的代码实现相同的功能
- 编解码繁琐,NIO本身不提供高级的编解码能力,开发者需要自己实现数据的序列化和反序列化
- 缺少内置协议的支持,不支持Http,Ftp协议的支持
Netty的优点
- 设计优雅:适用于各种传输类型的统一 API 阻塞和非阻塞 Socket;基于灵活且可扩展的事件模型,可以清晰地分离关注点;高度可定制的线程模型 - 单线程,一个或多个线程池.
- 性能优秀:Netty 设计用于处理大量的并发连接,并且能够在较低的资源消耗下实现高吞吐量,利用了操作系统级别的多路复用技术,减少了线程上下文切换的开销。
- 丰富的功能:
-
- 内置了许多常用的编解码器,支持多种协议,如 HTTP、WebSocket、SMTP
- Netty 支持 SSL/TLS 加密,确保数据传输的安全性
- Netty 提供了灵活的 ChannelPipeline 模型,允许开发者轻松地添加、删除或替换处理逻辑
- 强大的社区支持:Netty 拥有一个活跃的开源社区,提供了丰富的文档、教程和支持,社区贡献了大量的示例代码和插件,帮助开发者快速上手。
线程模型
传统阻塞 I/O (Blocking I/O)
- 特点:
-
- 每个连接由一个独立的线程处理。
- 线程在等待 I/O 操作完成时会被阻塞。
- 优点:
-
- 实现简单,易于理解。
- 缺点:
-
- 高并发情况下需要大量的线程,导致资源消耗大。
- 线程上下文切换开销高,影响性能。
2. 非阻塞 I/O (Non-blocking I/O)
- 特点:
-
- 使用 NIO 技术,I/O 操作不会阻塞线程。
- 通过选择器(Selector)管理多个通道(Channel),实现单线程或多线程处理多个连接。
- 优点:
-
- 更高效地处理大量并发连接。
- 减少线程数量和上下文切换开销。
- 缺点:
-
- 实现复杂,开发者需要手动管理状态和缓冲区。
- 调试困难,错误处理复杂。
3. 多路复用 I/O (Multiplexed I/O)
- 特点:
-
- 使用操作系统级别的 I/O 多路复用机制(如
select
、poll
、epoll
)来监视多个文件描述符。 - 单个线程可以同时处理多个连接的 I/O 事件。
- 使用操作系统级别的 I/O 多路复用机制(如
- 优点:
-
- 高效地处理大量并发连接。
- 减少了线程的数量和上下文切换。
- 缺点:
-
- 实现相对复杂。
- 性能受限于操作系统的 I/O 多路复用机制。
4. Reactor 模式
- 特点:
-
- 主要用于构建高并发服务器。
- 使用一个或多个 Reactor 线程来分派 I/O 事件到相应的处理器。
- 优点:
-
- 可以处理大量并发连接。
- 分离了 I/O 处理和业务逻辑处理。
- 缺点:
-
- 实现复杂,特别是对于复杂的业务逻辑。
- 错误处理和调试较为困难。
常见的 Reactor 模式的变体:
- 单线程 Reactor:
-
- 所有的 I/O 事件和业务逻辑都在一个线程中处理。
- 适合简单的应用场景,但不适合高负载场景。
- 多线程 Reactor:
-
- 使用多个工作线程来处理业务逻辑。
- 提高了吞吐量,但仍需处理线程同步问题。
- 主从 Reactor:
-
- 主 Reactor 处理新连接请求,从 Reactor 处理已建立连接的 I/O 事件。
- 提高了系统的可伸缩性和稳定性
1.Reactor主线程 MainReactor 对象通过select 监听连接事件, 收到事件后,通过Acceptor 处理连接事件
2.当 Acceptor 处理连接事件后,MainReactor 将连接分配给从Reactor
3.从Reactor 将连接加入到连接队列进行监听,并创建handler进行各种事件处理
4.当有新事件发生时, 从reactor 就会调用对应的handler处理
5.handler 通过read 读取数据,分发给后面的worker 线程处理
6.worker 线程池分配独立的worker 线程进行业务处理,并返回结果
7.handler 收到响应的结果后,再通过send 将结果返回给client
8.Reactor 主线程可以对应多个Reactor 子线程, 即MainRecator 可以关联多个SubReactor
Netty就是基于Reactor模式实现的线程模型。
Netty线程模型
工作原理
Netty抽象出两组线程池,Boss Group负责接收客户端的连接,Worker Group负责网络的读写,Boss Group和Worker Group的类型都是NioEventLoop
NioEventLoopGroup是一个事件循环组,这个组有多个线程,每个线程包含一个NioEventLoop
NioEventLoop表示一个不断执行任务的循环线程,每个NioEventLoop都有一个Selector,用于监听绑定在该通道上的socket网络通讯
BossNioEventLoop循环执行步骤:
1.轮询accept事件
2.处理accept事件,与客户端建立连接,生成NioSocketChannel,并将其注册某个worker NioEventLoop上的selector上
3.处理任务队列的任务,即runAllTasks
每个WorkerNioEventLoop循环执行步骤
1.轮询read/write事件
2.处理read/write事件,在对应的NioSocketChannel上处理
3.处理任务队列的任务,即runAllTasks
每个WorkerNioEventLoop处理业务时,会使用Pipeline,pipeline中包含了bossGroup上NioEventLoop注册到Worker的selector的channel,即通过pipeline可以获取到对应的channel,channel中维护了很多Hander
Netty ChannelHandler组件
ChannelHandler UML图如下:
Netty 中的 ChannelHandler 的作用是,在当前 ChannelHandler 中处理 IO 事件,并将其传递给 ChannelPipeline 中下一个 ChannelHandler 处理,因此多个 ChannelHandler 形成一个责任链,责任链位于 ChannelPipeline 中。
数据在基于 Netty 的服务器或客户端中的处理流程是:读取数据-->解码数据-->处理数据-->编码数据-->发送数据。其中的每个过程都用得到 ChannelHandler 责任链。