Java-66 深入浅出 分布式服务 Netty详解 EventLoop
点一下关注吧!!!非常感谢!!持续更新!!!
🚀 AI篇持续更新中!(长期更新)
AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究,持续打造实用AI工具指南!📐🤖
💻 Java篇正式开启!(300篇)
目前2025年07月07日更新到:
Java-65 深入浅出 分布式服务 网络通信 BIO NIO AIO 详解 附代码
MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!
📊 大数据板块已完成多项干货更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
Netty
基本介绍
Netty 是由 JBOSS 提供的一个高性能、异步的、基于事件驱动的网络应用框架。它建立在 Java NIO 技术之上,通过简化和抽象 NIO 的复杂性,让开发者能够专注于业务逻辑的实现,而无需处理底层网络通信的细节。
核心特性
- 异步非阻塞:采用 Reactor 线程模型,支持高并发连接
- 事件驱动:基于 Selector 机制实现高效的事件处理
- 零拷贝:提供优化的数据传输机制,减少内存拷贝
- 高可扩展性:模块化设计,支持灵活扩展
- 多种协议支持:内置 HTTP、WebSocket、TCP/UDP 等协议支持
应用场景
-
互联网领域:
- 高性能 RPC 框架(如 Dubbo)
- 分布式消息中间件
- 实时通信系统
-
大数据领域:
- 分布式计算框架通信层
- 数据采集传输系统
-
游戏行业:
- 多人联机游戏服务器
- 实时对战系统
-
通信行业:
- 物联网网关
- 电信级应用服务器
知名应用案例
- Elasticsearch:使用 Netty 作为其分布式节点间的通信组件
- Dubbo:基于 Netty 实现高效的 RPC 通信
- Spark:在部分网络模块中使用 Netty
- RocketMQ:消息队列的网络通信层基于 Netty 构建
Netty 成功解决了传统阻塞 IO 的性能瓶颈问题,并克服了原生 NIO API 复杂难用的缺点,成为构建高性能网络应用的理想选择。其精心设计的 API 和丰富的功能组件,使得开发者能够快速构建可扩展的、高性能的网络服务器和客户端。
为什么使用 Netty
NIO的缺点
复杂的使用方式
● NIO的类库和API设计复杂,使用门槛较高。开发者需要熟练掌握多个核心组件:
- Selector:用于监听多个通道的事件
- ServerSocketChannel:服务器端监听新连接的通道
- SocketChannel:网络套接字通道
- ByteBuffer:用于数据读写的缓冲区
- 示例:实现一个简单的NIO服务器就需要处理数十种边界情况和异常状态
开发难度大
● 可靠性保障困难,开发者需要:
- 处理各种网络异常和边界条件
- 实现复杂的线程同步机制
- 编写大量样板代码来维护连接状态
- 开发工作量通常比传统IO高出3-5倍
存在固有缺陷
● NIO存在一些难以避免的BUG:
- 著名的Epoll BUG会导致Selector在无事件时也持续轮询
- 在Linux环境下特别容易出现
- 会导致CPU占用率达到100%,严重影响系统性能
- 通常需要额外编写代码来检测和修复这个问题
Netty的优点
统一的API接口
● 提供统一的API支持多种传输协议:
- TCP/UDP
- HTTP/HTTPS
- WebSocket
- 自定义协议
- 示例:只需修改少量代码就能在不同协议间切换
灵活的线程模型
● 提供高度可定制的线程模型:
- 单线程模型:适用于低并发场景
- 多线程模型:利用线程池处理高并发
- 主从线程模型:分离连接接收和处理
- 自定义线程模型:根据业务需求灵活配置
- 典型应用:游戏服务器通常采用特定的线程模型优化性能
优异的性能表现
● 在性能方面具有显著优势:
- 吞吐量比传统方案提升30%-50%
- 延迟降低40%-60%
- 资源消耗减少20%-30%
- 测试数据:在相同硬件条件下可支持更多并发连接
高效的内存管理
● 采用先进的内存管理技术:
- 使用堆外内存减少GC压力
- 实现零拷贝技术
- 提供内存池化机制
- 示例:大文件传输时内存效率提升显著
线程模型
单线程模型
线程池模型
Netty 模型
Netty采用了高度优化的多线程模型,其核心线程池设计分为两个关键部分:
-
BossGroup线程池组:
- 专门负责处理客户端TCP连接请求
- 默认情况下,线程池大小设置为1(适用于多数场景)
- 每个线程都是一个NioEventLoop实例
- 示例场景:当服务器端口8080收到100个并发连接请求时,BossGroup会负责完成TCP三次握手过程
-
WorkerGroup线程池组:
- 专门负责处理已建立连接的I/O读写操作
- 线程数通常设置为CPU核心数×2(最佳实践配置)
- 每个Worker线程同样是一个NioEventLoop实例
- 典型应用:处理HTTP请求的内容读取、业务逻辑处理和响应返回
NioEventLoop的核心工作机制:
- 每个NioEventLoop线程维护一个Selector实例
- 采用事件驱动模型,持续监听注册的SocketChannel事件(如OP_READ、OP_WRITE)
- 内部采用串行化设计确保线程安全,处理流程包括:
- 网络数据读取(从Socket缓冲区)
- 字节流解码(如HTTP协议解码)
- 业务逻辑处理(如数据库查询)
- 响应数据编码(如将对象序列化为JSON)
- 数据发送回客户端(写入Socket缓冲区)
技术优势:
- 避免线程上下文切换:一个连接的所有操作都在同一个NioEventLoop中完成
- 无锁化设计:通过任务队列实现线程间通信
- 高性能:在4核服务器上可轻松支持数万并发连接
典型配置示例:
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 1个Boss线程
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 默认CPU核心数×2
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) {// 添加业务处理Handler}});
核心组件
ChannelHandler 及其实现类
ChannelHandler 接口定义了许多事件处理的方法,我们可以通过重写这些方法实现具体的业务逻辑。
我们经常需要自定义一个Handler类去集成 ChannelHandlerAdapter,然后通过重写相应方法实现业务逻辑,我们接下来看看一般都需要重写哪里方法:
● public void channelActive(ChannelHandlerContext ctx), 通道就绪事件
● public void channelRead(ChannelHandlerContext ctx, Object msg), 通道读取数据事件
● public void channelReadComplete(ChannelHandlerContext ctx) , 数据读取完毕事件
● public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause), 通道发生异常事件
ChannelPipeline
ChannelPipeline 是一个 Handler 的集合,它负责处理和拦截 inbound 或者 outbound 的事件和操作,相当于一个贯穿 Netty 的链。
● ChannelPipeline addFirst(ChannelHandler… handlers), 把一个业务处理类(handler) 添加到链中的第一
个位置
● ChannelPipeline addLast(ChannelHandler… handlers), 把一个业务处理类(handler) 添加到链中的最后
一个位置
ChannelHandlerContext
这是事件处理器上下文对象,Pipeline 链中的实际处理节点,每个处理节点 ChannelHandlerContext 中包含一个具体的事件处理器 ChannelHandler,同时 ChannelHandlerContext 中也绑定了对应的 Pipline 和 Channel 的信息,方便对 ChannelHandler 进行调用。
● ChannelFuture close(), 关闭通道
● ChannelOutboundInvoker flush(), 刷新
● ChannelFuture writeAndFlush(Object msg) , 将 数 据 写 到 ChannelPipeline 中 当 前
● ChannelHandler 的下一个 ChannelHandler 开始处理(出站)
ChannelFuture
表示 Channel 中异步 I/O 操作的结果,在 Netty 中所有的 I/O操作都是异步的,I/O的调用会直接返回,调用者并不能立刻获得结果,但是可以通过 ChannelFuture 来获取 I/O 操作的处理状态。
常用方法如下所示:
● Channel channel(), 返回当前正在进行 IO 操作的通道
● ChannelFuture sync(), 等待异步操作执行完毕
EventLoopGroup 和其实现类 NioEventLoopGroup
EventLoopGroup 是一组 EventLoop 的抽象,Netty 为了更好的利用多核CPU资源,一般会多个 EventLoop 同时工作,每个 EventLoop 维护着一个 Selector 实例。
EventLoopGroup 提供 next 接口,可以从组里面按照一定规则获取其中一个 EventLoop 来处理服务。在 Netty 服务器端编程中,我们一般需要提供两个 EventLoopGroup,例如:BossEventLoopGroup 和 WorkerEventLoopGroup。
● public NioEventLoopGroup(), 构造方法
● public Future<?> shutdownGracefully(), 断开连接, 关闭线程
ServerBootstrap 和 Bootstrap
ServerBootstrap 是 Netty 中的服务端启动助手,通过它可以完成服务端的各种配置,Bootstrap 是 Netty 中的客户端启动助手,通过它可以完成客户端的各种配置。
常用方法如下所示:
● public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup),该方法用于
服务器端, 用来设置两个 EventLoop
● public B group(EventLoopGroup group) , 该方法用于客户端, 用来设置一个 EventLoop
● public B channel(Class<? extends C> channelClass), 该方法用来设置一个服务器端的通道实现
● public B option(ChannelOption option, T value), 用来给 ServerChannel 添加配置
● public ServerBootstrap childOption(ChannelOption childOption, T value), 用来给接收到的 通道添加配置
● public ServerBootstrap childHandler(ChannelHandler childHandler), 该方法用来设置业务处理类(自定
义的 handler)
● public ChannelFuture bind(int inetPort) , 该方法用于服务器端, 用来设置占用的端口号
● public ChannelFuture connect(String inetHost, int inetPort) 该方法用于客户端, 用来连接服务器端