- 异步事件驱动的 NIO 框架,基于 Java NIO 提供的API实现
- 所有的 I/O 操作都是异步非阻塞的,通过 Future-Listener 机制,用户可以方便地主动获取或通过通知机制获取 I/O 操作结果
Reactor 线程模型
服务端在接收到客户端请求后采用多路复用策略,通过一个非阻塞的线程来异步接收所有的客户端请求,并将这些请求转发到相关的工作线程组上进行处理
Java NIO
三个核心概念: Selector(选择器)、Channel(通道)、Buffer(缓冲区)
Selector 用于监听多个 Channel 的事件
- 传统的 I/O 基于数据流进行 I/O 读写,而 Java NIO 基于 Channel 和 Buffer 进行 I/O 读写操作,并且数据总是从 Channel 读到 buffer,或从 Buffer 写入 Channel
- Selector 只在 Channel 上有读写事件发生时,才会调用 I/O 函数进行读写操作,可极大地减少系统开销,提高并发量
- Channel
- 数据流管道
- FileChannel:文件的 I/O 操作
- DatagramChannel:UDP 广播事件
- SocketChannel:Socket 客户端 TCP 的读写
- ServerSocketChannel: Socket 服务端的 TCP 的读写
Buffer
- 容器,其内部通过一个连续的字节数组存储 I/O 上的数据
- 在 NIO 中,Channel 对数据的读写都必须经过 Buffer
线程模型
Reactor 单线程模型
Accept 接收客户端的 TCP 连接请求消息
链路建立成功后通过 Dispatcher 将接收到的消息写入 ByteBuffer,并派发到对应的 DecoderHandler进行解码和处理
消息处理完成后调用对应的EncoderHandler将请求响应进行消息的编辑和下发
Reactor 多线程模型
用于接收客户端请求的 Acceptor 由一个线程负责,用户处理客户端消息的 Dispatcher 由一个线程池负责
Reactor 主从多线程模型核心组件
Bootstrap/ServerBootstrap:Bootstrap 用于客户端服务的启动引导,ServerBootstrap 用于服务端服务的启动引导
- NioEventLoop:基于线程队列的方式执行事件操作,具体要执行的事件操作包括连接注册、端口绑定和I/O数据读写等。每个NioEventLoop线程都负责多个Channel的事件处理
- NioEventLoopGroup:NioEventLoop生命周期的管理
- Future/ChannelFuture:Future和ChannelFuture用于异步通信的实现,基于异步通信方式可以在I/O操作出发后注册一个监听事件,在I/O操作完成后自动触发监听事件并完成后续操作
- Channel:用于执行具体的 I/O操作
- Selector:用于多路复用中 Channel 的管理,在内部监听每个Channel上 I/O 事件的变化,当 Channel 有网络I/O事件发生时通知 ChannelHandler 执行具体的 I/O 操作
- ChannelHandler:I/O事件的拦截和处理。分为ChannelInboundHandler 和 ChannelOutboundHandler
- ChannelHandlerContext:Channel 上下文信息的管理。每个 ChannelHandler都对应一个ChannelHandlerContext
- ChannelPipeline:基于拦截器设计模式实现的事件拦截处理和转发。每个 Channel 都对应一个 ChannelPipeline,在ChannelPipeline中维护了一个由 ChannelHandlerContext 组成的双向链表,每个 ChannelHandlerContext 都对应一个 ChannelHandler,以完成具体 Channel 事件的拦截和处理
原理
Netty 的运行核心包含两个 NioEventLoopGroup 工作组
一个是 BossGroup,用于接收客户端连接、接收客户端数据和进行数据转发
一个是 WorkerGroup,用于具体的 I/O 事件的触发和数据处理服务端的初始化步骤
- 初始化 BossGroup 和 WorkerGroup
- 基于 ServerBootstrap 配置 EventLoopGroup,包括连接参数设置、Channel 类型设置、编解码 Handler 设置等
- 绑定端口和服务启动
BossGroup 职责
一个事件循环组,其中包含多个事件循环(NioEventLoop),每个 NioEventLoop都包含一个Selector和一个 TaskQueue(事件循环线程)
每个 Boss NioEventLoop 循环执行以下3个步骤
每个 Worker NioEventLoop 循环执行以下3个步骤
大量并发请求 —- I/O 多路复用模型 —- Selector 和 Channel
数据零拷贝
- 传统的 Socket 服务基于 JVM 堆内存进行 Socket 读写,即 申请内存资源时,需要通过 JVM 向操作系统申请堆内存,然后 JVM 将堆内存复制一份到直接内存中,基于直接内存进行 Socket 读写。这样就存在频繁的 JVM 内存数据和 Socket 线程内存数据来回复制的问题,影响系统性能
- Netty 的数据接收和发送均采用堆外直接内存进行 Socket 读写,堆外直接内存可以直接操作操作系统内存,不需要来回进行字节缓冲区的二次复制,大大提高了系统的性能
- Netty 的文件传输采用 transferTo 方法。 transferTo 方法可以直接将文件缓冲区的数据基于内存映射技术发送到目标 Channel,避免了通过循环写方式导致的内存复制问题
内存重用机制
- JVM 对于对象的分配和回收耗时很小,但 Netty 数据的接收和发送均采用堆外直接内存缓存的方式实现,而堆外直接内存缓存的分配和回收是一件耗时的操作。为了尽量重用缓冲区,Netty 提供了基于内存池的缓冲重用机制
无锁化设计
- 朵多线程会提高系统的并发度,但是线程数并不是越多越好,过多的线程会引起 CPU 的频繁切换而增加系统的负载
高性能的序列化框架
- Netty 默认基于 Google ProtoBuf 实现数据的序列化
