Netty:异步的,基于事件驱动的网络应用框架,快速开发高性能的服务端和客户端
一、Netty 简介
- 简介:异步的,基于事件驱动的网络应用框架,快速开发高性能的服务端和客户端
- Core
- Zero-Copy-Capable Rich Byte Buffer:零拷贝
- Universal Communication API:交互 API
- Extensible Event Model:可扩展事件模型
- Protocol Support
- HTTP & WebSocket、SSL-StartTLS、Google Protobuf、zlib/gzip、Large File Transfer、RTSP…
- Transport Services
- Socket & Datagram
- HTTP Tunnel
- In-VM Pipe
- Core
- 官网说明
- 由 JBOSS 提供的一个 Java 开源框架,Netty 提供异步的、基于事件驱动的网络应用程序框架,用于快速开发高性能、高可靠的网络 IO 程序
- Netty 可以帮助你快速、简单的开发出一个网络应用,相当于简化和流程化了 NIO 的开发过程
- Netty 是目前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得广泛的应用
- Elasticsearch、Dubbo 框架内部都采用 Netty
- 优点
- 涉及优雅:适用各种传输类型的统一 API 阻塞和非阻塞 Socket
- 使用方便:没有其他依赖项,JDK 5 / 6 就足够了
- 高性能、吞吐量更高、延迟更低、减少资源消耗、最小化不必要的内存复制
- 安全:完整的 SSL/TLS 和 StartTLS 支持
- 社区活跃…
- 版本说明
- 3.x、4.x、5.x
- 5.x 出现重大 Bug 被废弃,常用 Netty4.x
- 3.x、4.x、5.x
二、线程模型
2.1 传统阻塞 I/O 服务模型
- 特点
- 采用阻塞 IO 模式获取输入数据
- 每个连接都需要独立的线程完成数据输入、业务处理、数据返回
- 问题
- 并发数很大,就会创建大量的线程,占用很大系统资源
- 连接创建后,当前线程如果没有数据可读,该线程会阻塞在 read 操作,造成线程资源浪费
2.2 Reactor 模式
- 解决 传统 IO 问题、基于 IO 复用模型、基于线程池复用线程资源
- 单 Reactor 单线程
- 方案
- Select 可以实现应用程序通过一个阻塞对象监听多路连接请求
- Reactor 对象通过 Select 监控客户端请求事件,收到事件后进行 Dispatch 分发
- 建立的是连接请求,由 Acceptor 通过 Accept 处理连接请求并创建一个 Handler 对象处理连接完成后的后续业务
- 不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应
- Handler 会完成 Read -> 业务处理 -> Send
- 优点:模型简单,没有多线程、进程通信、竞争问题全在一个线程完成
- 缺点:性能问题,只有一个线程,无法发挥 CPU 性能,很容易导致性能瓶颈,可靠性问题
- 使用场景:客户端有限,业务处理迅速
- 方案
- 单 Reactor 多线程
- 方案
- Reactor 对象通过 select 监控客户请求事件,收到后通过 dispatch 进行分发
- 如果建立连接请求,由 Acceptor 通过 accept 进行处理,建立一个 Handler 对象处理完成各种事件
- 不是连接请求,由 reactor 分发对应的 handler
- handler 只负责响应,不做具体业务处理,通过 read 读取数据后,分发给后面的 worker 线程池的某个线程处理
- worker 线程池分配独立线程完成真正的业务,并返回结果给 handler
- handler 收到响应后,通过 send 将结果返回 client
- 优点:充分利用 CPU 多核的处理能力
- 缺点:多线程数据共享和访问比较复杂,reactor 处理所有事件的监听和响应,单线程运行很容易出现性能瓶颈
- 方案
- 主从 Reactor 多线程
- 方案
- Reactor 主线程 MainReactor 对象通过 select 监听连接事件,收到后通过 Acceptor 处理连接事件
- 处理连接事件后,MainReactor 将连接分配给 SubReactor
- SubReactor 将连接加入到连接队列进行监听,并创建 handler 进行事件处理
- handler 通过 read 读取数据,分发给后面的 worker 线程处理
- worker 线程池分配独立的 worker 线程进行业务处理,并返回结果
- handler 收到响应的结果后,再通过 send 将结果返回给 client
- 优点:父线程和子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理
- 缺点:编程复杂度高
- 方案
- 说明
- Reactor 模式,通过一个或多个输入同时传递给服务处理器的模式(基于事件驱动)
- 服务器端程序处理传入的多个请求,并将他们同步分派到相应的处理线程,因此 Reactor 模式也叫 Dispatcher 模式
- Reactor 模式使用 IO 复用监听事件,收到事件后,分发给某个线程(进程),这就是网络高并发处理关键
- 核心组成
- Reactor:在一个单独的线程运行,复制监听和分发事件,分发给适当的处理程序来对 IO 事件做出反应
- Handlers:处理程序执行 IO 事件要完成的实际事件,Reactor 通过调度适当的处理程序来响应 IO 事件,处理程序执行非阻塞操作
- 优点:响应快、最大程度避免复杂的多线程及同步问题、扩展性好、复用性好
三、Netty 模型
- 简单版方案
- BossGroup 线程维护 Selector,只关注 Accept
- 接收到 Accept 事件,获取对应的 SocketChannel 封装成 NIOSocketChannel 并注册到 Worker 线程(事件循环)中进行维护
- 当 Worker 线程监听到 selector 中有感兴趣到事件后,就进行处理(由 handler),注意 handler 以及加入到通道
- 详细版方案
- Netty 抽象出两组线程池 BossGroup 专门负责接收客户端的连接,WorkerGroup 专门负责网络的读写
- BossGroup 和 WorkerGroup 类型都是 NIOEventLoopGroup
- NIOEventLoopGroup 相当于一个事件循环组,这个组中包含多个事件循环,每一个事件循环是 NIOEventLoop
- NIOEventLoop 表示一个不断循环的执行处理任务的线程,每个 NIOEventLoop 都有一个 selector,用于监听绑定在其上的 socket 的网络通讯
- NIOEventLoopGroup 可以有多个线程,可以含有多个 NIOEventLoop
- 每个 Boss NIOEventLoop 循环执行的步骤有三步
- 轮询 accept 事件
- 处理 accept 事件,与 client 建立连接,生成 NIOSocketChannel,并注册到某个 worker NIOEventLoop 上的 selector
- 处理任务队列的任务,即 runAllTasks
- 每个 Worker NIOEventLoop 循环执行的步骤
- 轮询 read、write 事件
- 处理 IO 事件,即 read、write 事件,在对应的 NIOSocketChannel 处理
- 处理任务队列的任务,即 runAllTasks
- 每个 Worker NIOEventLoop 处理业务时,会使用 pipeline,包含了 channel,即通过 pipeline 可以获取到对应的管道,维护了很多处理器
- 任务队列中的 Task 使用场景
- 用户自定义的普通任务
- 用户自定义的定时任务
- 非当前 Reactor 线程调用 Channel 的各种方法
- 方案再说明
- Netty 抽象出两组线程池,BossGroup 专门负责接收客户端连接,WorkerGroup 专门负责网络读写操作
- NioEventLoop 表示一个不断循环执行任务处理的线程,每个 NioEventLoop 都有一个 selector,用于监听绑定在其上的 socket 网络通道
- NioEventLoop 内部采用串行化设计,从消息的读取 -> 解码 -> 编码 -> 发送,始终由 IO 线程 NioEventLoop 负责
- NioEventLoopGroup 下包含多个 NioEventLoop
- 每个 NioEventLoop 中包含有一个 Selector,一个 taskQueue
- 每个 NioEventLoop 的 Selector 上可以注册监听多个 NioChannel
- 每个 NioChannel 之后绑定在唯一的 NioEventLoop 上
- 每个 NioChannel 都绑定有一个自己的 ChannelPipeline
- 异步模型
- 基本介绍
- 异步过程调用发出后,不能立刻得到结果,实际处理在完成后,通过状态、通知和回调来通知调用者
- Netty 中的 IO 操作是异步的,包括 Bind、Write、Connect 等操作会简单返回一个 ChannelFuture
- 调用者不能立刻得到结果,需要通过 Future-Listener 机制,用户可以方便的主动获取或者通知机制获取 IO 操作结果
- 建立在 future 和 callback 之上
- future 核心思想:一个方法计算过程可能非常耗时,调用的时候会返回一个 Future,通过 Future 去监控方法处理过程
- Future 说明
- 表示异步的执行结果,可以通过提供的方法来检测执行是否完成,比如检索计算等
- ChannelFuture 是一个接口,可以添加监听器,当监听的事件发生时,就会通知监听器
- Future-Listener 机制
- Future 对象刚创建时,处于非完成状态,可以通过返回的 ChannelFuture 来获取操作执行的状态,注册监听函数来执行完成后的操作
- 常见操作
- isDone:判断是否完成
- isSuccess:判断是否成功
- getCause:获取当前操作失败原因
- isCancelled:是否被取消
- addListener:注册监听器,操作完成会通知指定的监听器,Future 对象已完成,则会通知指定的监听器
- 基本介绍
- 小结:相比传统阻塞 IO,异步处理不会造成线程阻塞,操作期间可以执行其他程序,高并发下会更稳定和更高的吞吐量