Netty从0到1系列之Netty整体架构、入门程序
文章目录
- 一、Netty概述
- 1.1 官网资料
- 1.2 概述
- 1.3 为什么选择Netty?
- 二、Netty的I/O模型
- 2.1 I/O模型的核心思想
- 2.2 Netty 的 I/O 模型演进:三种 Reactor 模式
- 2.2.1 单 Reactor 单线程模型(Simple)
- 2.2.2 单 Reactor 多线程模型(Moderate)
- 2.2.3 主从 Reactor 多线程模型(Recommended)✅
- 2.3 Netty I/O 模型的底层优化
- 2.4 I/O 模型对比总结
- 2.5 Netty I/O 模型的核心价值
- 2.6 📕一句话总结
- 三、Netty整体架构解析
- 3.1 整体架构
- 3.2 架构设计
- 3.2.1 Netty架构全景图
- 3.2.2 Netty的分层架构设计
- 3.3 Netty架构设计精髓总结
- 3.4 Netty架构的优缺点分析
- 3.5 Netty架构设计的核心价值
- 3.6 一句话总结
- 四、Netty开发环境搭建
- 4.1 引入依赖
- 4.2 配置logback.xml文件
- 五、入门程序
- 5.1 服务器端
- 5.2 客户端
- 5.3 程序解析
- 先建立整体认知,不必追求细节.
- 先建立整体认知,不必追求细节.
- 先建立整体认知,不必追求细节.
一、Netty概述
1.1 官网资料
https://netty.io/
Netty: Home
Netty 是一个异步事件驱动的网络应用框架
用于快速开发可维护的高性能协议服务器和客户端。
1.2 概述
Netty 是一个 NIO 客户端服务器框架,可以快速轻松地开发网络应用程序,例如协议服务器和客户端。它极大地简化和简化了网络编程,例如 TCP 和 UDP 套接字服务器。
“快速简单”并不意味着生成的应用程序将受到可维护性或性能问题的影响。Netty 是根据从许多协议(如 FTP、SMTP、HTTP 以及各种基于二进制和文本的遗留协议)的实施中获得的经验精心设计的。因此,Netty 成功地找到了一种方法,可以在不妥协的情况下实现开发的便利性、性能、稳定性和灵活性。
1.3 为什么选择Netty?
Netty 是一个基于 Java NIO 的 异步事件驱动 的高性能网络应用框架,广泛应用于构建高并发、低延迟的协议服务器与客户端,如 RPC 框架(Dubbo、gRPC)、消息中间件(RocketMQ)、即时通讯系统等。
❌ 原生 NIO 编程的复杂性
问题 | 说明 |
---|---|
API 复杂 | 需手动管理 Selector 、Channel 、Buffer 状态 |
空轮询 Bug | JDK NIO 存在 select() 无限返回 0 的问题 |
内存管理困难 | Buffer 分配、释放、泄漏风险高 |
编解码复杂 | 协议解析需手动实现粘包/拆包 |
线程模型复杂 | Reactor 模式需自行实现 |
🎯 Netty 的目标:简化网络编程,屏蔽底层复杂性,提供高性能、高可扩展性的 API
✅ 优点
优点 | 说明 |
---|---|
高性能 | 无锁串行化、内存池、零拷贝 |
高并发 | 支持数十万甚至百万连接 |
高可扩展 | ChannelHandler 管道模式,易于扩展 |
API 简洁 | 封装了 NIO 复杂性 |
协议丰富 | 支持 HTTP、WebSocket、SSL/TLS 等 |
社区活跃 | 文档齐全,问题易解决 |
被广泛采用 | Dubbo、RocketMQ、Elasticsearch 等 |
二、Netty的I/O模型
2.1 I/O模型的核心思想
✅ 核心目标
- 单线程处理多个连接(高并发)
- 无阻塞 I/O 操作(低延迟)
- 事件驱动处理(高响应性)
- 避免锁竞争(高性能)
🎯 设计哲学
“让一个线程负责一个事件循环,避免上下文切换和锁开销。”
2.2 Netty 的 I/O 模型演进:三种 Reactor 模式
Netty 支持三种经典的 Reactor 模型,可通过配置灵活切换。
2.2.1 单 Reactor 单线程模型(Simple)
- 特点:
- 所有操作(连接、读、写、业务)由同一个线程完成
- 适合低并发、调试场景
EventLoopGroup group = new NioEventLoopGroup(1);
bootstrap.group(group) // 单线程
⚠️ 不推荐用于生产环境:业务处理阻塞会拖慢整个 I/O 线程。
2.2.2 单 Reactor 多线程模型(Moderate)
-
特点
Main Reactor
:专门处理accept
新连接Sub Reactor
:多个线程处理 I/O 读写- 业务逻辑仍在 I/O 线程中执行
-
适用场景:中等并发,业务处理轻量
⚠️ 风险:若业务处理耗时,仍会阻塞 I/O 线程。
2.2.3 主从 Reactor 多线程模型(Recommended)✅
-
特点:
Boss Group
:1 个或少量线程,只负责接收连接Worker Group
:多个线程,只负责 I/O 读写- 业务处理通过
ctx.executor().execute()
提交到独立的业务线程池
-
优势:
- I/O 线程永不阻塞
- 最大化吞吐量
- 生产环境推荐模式
2.3 Netty I/O 模型的底层优化
✅ 1. 修复 JDK NIO 空轮询 Bug
- JDK NIO 存在
select()
无限返回 0 的问题 - Netty 通过 计数检测 + 重建 Selector 修复
✅ 2. 无锁串行化设计
- 每个
Channel
绑定到固定EventLoop
- 所有操作由同一线程执行,无需加锁
✅ 3. 任务调度优化
taskQueue
:普通任务(如channel.write()
)scheduledTaskQueue
:定时任务(如心跳)- 避免
ScheduledExecutorService
的锁竞争
2.4 I/O 模型对比总结
模型 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
单 Reactor 单线程 | 简单、无锁 | 并发低、易阻塞 | 学习、调试 |
单 Reactor 多线程 | 并发提升 | 业务阻塞 I/O 线程 | 中低并发 |
主从 Reactor 多线程 | 高并发、高吞吐 | 稍复杂 | 生产环境推荐 |
2.5 Netty I/O 模型的核心价值
维度 | 说明 |
---|---|
核心模式 | 主从 Reactor 多线程模型 |
关键优势 | 异步、非阻塞、无锁、高并发 |
性能保障 | 事件循环 + 任务队列 + 内存池 |
设计精髓 | “一个线程一个事件循环” |
适用场景 | 高性能 RPC、网关、消息系统 |
2.6 📕一句话总结
💡 一句话总结:
Netty 的 I/O 模型通过 主从 Reactor 架构 和 无锁串行化设计,实现了 单机百万级并发连接 的能力,是现代高性能网络服务的标准范式。
三、Netty整体架构解析
3.1 整体架构
Core层
- Core 核心层是 Netty 最精华的内容,它提供了底层网络通信的通用抽象和实现,包括可扩展的事件模型、通用的通信 API、支持零拷贝的 ByteBuf 等
Protocol Support 协议支持层
- 协议支持层基本上覆盖了主流协议的编解码实现,如 HTTP、SSL、Protobuf、压缩、大文件传输、WebSocket、文本、二进制等主流协议,此外 Netty 还支持自定义应用层协议。Netty 丰富的协议支持降低了用户的开发成本,基于 Netty 我们可以快速开发 HTTP、WebSocket 等服务。
Transport Service 传输服务层
传输服务层提供了网络传输能力的定义和实现方法。它支持 Socket、HTTP 隧道、虚拟机管道等传输方式。Netty 对 TCP、UDP 等数据传输做了抽象和封装,用户可以更聚焦在业务逻辑实现上,而不必关系底层数据传输的细节。
Netty 的模块设计具备较高的通用性和可扩展性,它不仅是一个优秀的网络框架,还可以作为网络编程的工具箱。Netty 的设计理念非常优雅,值得我们学习借鉴。
3.2 架构设计
Netty 不仅仅是一个网络通信库,它是一个高度模块化、可扩展、高性能的异步事件驱动网络应用框架。其架构设计融合了多种经典设计模式与系统优化思想,是现代高性能网络编程的典范。
本文将从 分层架构、核心组件、数据流、线程模型、扩展机制 五个维度,全面深入剖析 Netty 的整体架构设计。
3.2.1 Netty架构全景图
3.2.2 Netty的分层架构设计
Netty 采用清晰的 分层架构,各层职责分明,耦合度低。
1. 应用层
(Application Layer)
- 职责:实现具体业务逻辑
-
组件:
ChannelHandler
、ChannelInboundHandler
、ChannelOutboundHandler
-
特点:开发者主要编写代码的层级
2. 核心框架层
(Core Framework)
- 职责:事件调度、I/O 处理、线程管理
- 组件
ChannelPipeline
:事件处理管道Channel
:网络连接抽象EventLoop
:事件循环EventLoopGroup
:线程组ByteBuf
:内存管理
3. 传输层
(Transport Layer)
- 职责:提供多种 I/O 模型支持
- 实现
NIO
:基于 JDK NIO(NioEventLoopGroup
)Epoll
:Linux 专属高性能(EpollEventLoopGroup
)OIO
:阻塞 I/O(已不推荐)UDT
:UDP-based Data Transfer
4. 协议编解码层
(Codec & Protocol)
- 职责:协议解析与生成
- 组件
ByteToMessageDecoder
:字节流 → 消息对象MessageToByteEncoder
:消息对象 → 字节流- 内置编解码器:
LengthFieldBasedFrameDecoder
、StringEncoder
等 - 协议支持:HTTP、WebSocket、SSL/TLS、Protobuf
5. 工具层
(Utility Layer)
- 职责:提供通用能力
- 组件
ByteBufAllocator
:内存分配器(支持池化)Future
/Promise
:异步结果通知Bootstrap
/ServerBootstrap
:启动辅助类ReferenceCountUtil
:引用计数工具
3.3 Netty架构设计精髓总结
设计原则 | 说明 | 实现方式 |
---|---|---|
单一职责 | 每个组件只做一件事 | Channel 、Pipeline 、EventLoop 分离 |
开闭原则 | 对扩展开放,对修改关闭 | ChannelHandler 管道模式 |
依赖倒置 | 高层依赖抽象 | Channel 接口,多种实现(NIO/Epoll) |
无锁设计 | 避免锁竞争 | 无锁串行化 + EventLoop 绑定 Channel |
异步非阻塞 | 高并发基础 | Future/Promise + 事件驱动 |
资源复用 | 减少 GC | 内存池、对象池 |
3.4 Netty架构的优缺点分析
✅ 优点
优点 | 说明 |
---|---|
高性能 | 无锁串行化、内存池、零拷贝 |
高可扩展 | ChannelHandler 可插拔 |
高可靠性 | 成熟稳定,被主流框架广泛采用 |
协议丰富 | 内置 HTTP、WebSocket、SSL 等 |
跨平台 | 支持 NIO(通用)、Epoll(Linux 优化) |
易测试 | EmbeddedChannel 支持单元测试 |
❌ 缺点
缺点 | 说明 |
---|---|
学习成本高 | Reactor 模型、事件驱动需理解 |
内存管理复杂 | ByteBuf 需手动释放,易泄漏 |
调试困难 | 异步堆栈不直观 |
过度设计 | 简单场景可能不必要 |
3.5 Netty架构设计的核心价值
Netty 的架构设计是“简单、高效、可扩展”哲学的完美体现。
维度 | 说明 |
---|---|
核心思想 | 异步事件驱动 + 无锁串行化 + 责任链模式 |
关键技术 | Reactor 模型、内存池、零拷贝、引用计数 |
设计目标 | 单机百万连接、微秒级延迟、高吞吐量 |
行业地位 | Java 高性能网络编程的事实标准 |
适用场景 | RPC、网关、消息系统、游戏服务器、物联网 |
3.6 一句话总结
💡 一句话总结:
Netty 通过 分层架构、组件化设计 和 极致优化,构建了一个高性能、高可靠、高可扩展的网络通信平台,是现代分布式系统不可或缺的基础设施。
四、Netty开发环境搭建
4.1 引入依赖
<properties><maven.compiler.source>24</maven.compiler.source><maven.compiler.target>24</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencies><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.18</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.16</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.5.18</version></dependency><!-- https://mvnrepository.com/artifact/io.netty/netty-all --><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.112.Final</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.38</version></dependency>
</dependencies>
4.2 配置logback.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configurationxmlns="http://ch.qos.logback/xml/ns/logback"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd"><!-- 输出控制,格式控制--><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%date{HH:mm:ss} [%-5level] [%thread] %logger{17} - %m%n </pattern></encoder></appender><!-- 用来控制查看那个类的日志内容(对mybatis name 代表命名空间) --><logger name="cn.tcmeta" level="DEBUG" additivity="false"><appender-ref ref="STDOUT"/></logger><logger name="io.netty.handler.logging.LoggingHandler" level="DEBUG" additivity="false"><appender-ref ref="STDOUT"/></logger><root level="ERROR"><appender-ref ref="STDOUT"/></root>
</configuration>
五、入门程序
5.1 服务器端
package cn.tcmeta.demo01;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import lombok.extern.slf4j.Slf4j;/*** @author: laoren* @description: Netty入门案例* @version: 1.0.0*/
@Slf4j
public class NettyServerDemo01 {public static void main(String[] args) {// 1. 创建服务器new ServerBootstrap()// 2. 创建NioEventLoopGroup, 现在可以简单理解为: 线程池 + Selector.// BossEventLoop, WorkerEventLoop(select, thread).group(new NioEventLoopGroup())// 3. 选择服务Socket实现类, 其中NioServerSocketChannel, 表示NIO的服务器端实现..channel(NioServerSocketChannel.class)// 4. 接下来添加的处理器都是给SocketChannel用的, 而不是给ServetSocketChannel用的.// ChannelInitializer处理器(仅执行一次),它的作用是待客户端SocketChannel建立连接后, 执行// initChannel以便添加更多的处理器.// boss处理连接, child(worker)进行读写操作.所以这里child添加的是处理SocketChannel的主要逻辑.// 执行操作封装为handler(处理器).childHandler(new ChannelInitializer<NioSocketChannel>() {// 5. 执行添加处理器@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception { // ChannelInitializer, 初始化channel, 添加各种各样的handler// 7. 处理SocketChannel, 解码 ByteBuf ---> Stringch.pipeline().addLast(new StringDecoder());// 8. 处理SocketChannel业务处理器, 使用上一个处理器的处理结果.ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println(msg);}});}// 6. 绑定服务器端口号}).bind(8888);log.debug("服务器已经启动, 监听端口号为 8888 ................");}
}
5.2 客户端
package cn.tcmeta.demo01;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;import java.util.Date;/*** @author: laoren* @description: 客户端测试* @version: 1.0.0*/
public class ClientDemo01 {public static void main(String[] args) throws InterruptedException {new Bootstrap()// 创建NioEventLoopGroup, 可以理解为线程池 + Selector.group(new NioEventLoopGroup())// 2..channel(NioSocketChannel.class).handler(new ChannelInitializer<Channel>() {@Overrideprotected void initChannel(Channel ch) throws Exception {ch.pipeline().addLast(new StringEncoder()); // 设置编码器}}).connect("127.0.0.1", 8888).sync().channel()// 向服务器发送数据.writeAndFlush(new Date() + ", hello netty!");}
}
服务器输出日志
5.3 程序解析
channel
可以理解为数据传输的通道.- 将
msg
理解为流动的数据
, 原始数据是ByteBuf
, 经过pipeline
的加工, 会将其变成类型对象
, 最后输出又变成ByteBuf
对象.handler
理解为数据处理的工序
- 工序有多道,合在一起就构成了
pipeline
,pipeline
负责发布事件【读、取完成等】传播每个handler
,handler
对自己感兴趣的事件进行处理【重写相应事件处理的方法】handler
分为Inbound
和OutBound
两类.- 将
eventLoop
理解为处理数据的工人
- 工人可以同时管理多个
channel
的io
操作,并且一旦工作负责了某个channel
,就要负责到底【绑定】- 工人既可以执行 io 操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个 channel 的待处理任务,任务分为普通任务、定时任务
- 工人按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每道工序指定不同的工人